记录一下java中ssrf的审计。
原理
服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
大部分的web服务器架构中,web服务器自身都可以访问互联网和服务器所在的内网。
作用
对外网服务器所在的内网、本地进行端口扫描,获取一些服务的banner信息 。
攻击运行在内网或者本地的应用程序。
对内网web应用进行指纹识别,通过访问默认文件实现 。
攻击内外网的web应用。sql注入、struct2、redis等。
利用file协议读取本地文件等。
php ssrf中的伪协议:
file dict sftp ldap tftp gopher
Java ssrf 中的伪协议:
file ftp mailto http https jar netdoc
@WebServlet("/ssrfServlet") public class ssrfServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String url = request.getParameter("url"); //接收url的传参 String htmlContent; PrintWriter writer = response.getWriter(); //获取响应的打印流对象 URL u = new URL(url); //实例化url的对象 try { URLConnection urlConnection = u.openConnection();//打开一个URL连接,并运行客户端访问资源。 HttpURLConnection httpUrl = (HttpURLConnection) urlConnection; //强转为HttpURLConnection BufferedReader base = new BufferedReader(new InputStreamReader(httpUrl.getInputStream(), "UTF-8")); //获取url中的资源 StringBuffer html = new StringBuffer(); while ((htmlContent = base.readLine()) != null) { html.append(htmlContent); //htmlContent添加到html里面 } base.close(); writer.println(html);//响应中输出读取的资源 writer.flush(); } catch (Exception e) { e.printStackTrace(); writer.println("请求失败"); writer.flush(); } }
在代码中HttpURLConnection httpUrl = (HttpURLConnection) urlConnection;
,这个地方进行了强制转换
区别在于:
URLConnection:可以走邮件、文件传输协议。 HttpURLConnection 只能走浏览器的HTTP协议
也就是说使用了强转为HttpURLConnection后,利用中只能使用http协议去探测该服务器内网的其他web应用。
如果读取文件会读取不出来,这就是因为协议限制
如果不强制转换成HttpURLConnection:
@WebServlet("/ssrfServlet") public class ssrfServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String url = request.getParameter("url"); //接收url的传参 String htmlContent; PrintWriter writer = response.getWriter(); //获取响应的打印流对象 URL u = new URL(url); //实例化url的对象 try { URLConnection urlConnection = u.openConnection();//打开一个URL连接,并运行客户端访问资源。 // HttpURLConnection httpUrl = (HttpURLConnection) urlConnection; //强转为HttpURLConnection BufferedReader base = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8")); //获取url中的资源 StringBuffer html = new StringBuffer(); while ((htmlContent = base.readLine()) != null) { html.append(htmlContent); //htmlContent添加到html里面 } base.close(); writer.println(html);//响应中输出读取的资源 writer.flush(); } catch (Exception e) { e.printStackTrace(); writer.println("请求失败"); writer.flush(); }
发现可以读取成功
@WebServlet("/readfileServlet") public class downloadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String url = request.getParameter("url"); int len; OutputStream outputStream = response.getOutputStream(); URL file = new URL(url); byte[] bytes = new byte[1024]; InputStream inputStream = file.openStream(); while ((len = inputStream.read(bytes)) > 0) { outputStream.write(bytes, 0, len); } } }
和上面的代码对比一下,发现其实都大致相同,唯一不同的地方是一个是用openStream方法获取对象,一个是用openConnection获取对象。两个方法类似。
官方说明文档:
openConnection():返回一个实例,该实例表示与所引用的远程对象的连接。 返回类型: URLConnection openStream():打开与此连接,并返回一个值以从该连接读取。 返回类型: InputStream
详细说明:
openConnection:返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。每次调用此URL的协议处理程序的openConnection方法都打开一个新的连接。如果URL的协议(例如,HTTP或JAR)存在属于以下包或其子包之一的公共、专用URLConnection子类:java.lang、java.io、java.util、java.net,返回的连接将为该子类的类型。例如,对于HTTP,将返回HttpURLConnection,对于JAR,将返回JarURLConnection。(返回到该URL的URLConnection!) openStream():打开到此URL的连接并返回一个用于从该连接读入的InputStream。
@WebServlet("/downloadServlet") public class downloadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String filename = "1.txt"; String url = request.getParameter("url"); response.setHeader("content-disposition", "attachment;fileName=" + filename); int len; OutputStream outputStream = response.getOutputStream(); URL file = new URL(url); byte[] bytes = new byte[1024]; InputStream inputStream = file.openStream(); while ((len = inputStream.read(bytes)) > 0) { outputStream.write(bytes, 0, len); } } }
ssrf中的文件下载和文件读取不同点在于响应头。
response.setHeader("content-disposition", "attachment;fileName=" + filename);
这段代码,设置mime类型为文件类型,访问浏览器的时候就会被下载下来。
imageIO类是jdk中自带的一个类,主要用于操作一些图片文件。比如读写、压缩图片。
@WebServlet("/httpclientServlet") public class httpclientServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String geturl = request.getParameter("url"); ServletOutputStream outputStream = response.getOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream(); URL url = new URL(geturl); BufferedImage image = ssrf.read(url); ImageIO.write(image, "png", os); InputStream input = new ByteArrayInputStream(os.toByteArray()); int len; byte[] bytes = new byte[1024]; while ((len = input.read(bytes)) > 0) { outputStream.write(bytes, 0, len); } } private static BufferedImage read(URL url) throws IOException{ if (url == null) { System.out.println("输入内容为空"); } InputStream istream = url.openStream(); ImageInputStream stream = ImageIO.createImageInputStream(istream); //获取文件流 BufferedImage bi = ImageIO.read(stream); //返回 BufferedImage作为供给的解码结果 return bi; } }
读取本地图片:
读取文件会显示500,所以这个点只能读取图片
CloseableHttpClient httpClient = HttpClients.createDefault(); HttpClient client = new HttpClient(); HttpGet getRequest = new HttpGet(url); HttpResponse response = httpClient.execute(getRequest);
Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build(); Response response = client.newCall(request).execute();
HttpRequest request = HttpRequest.get("http://www.baidu.com",true,'q',"baseball gloves","size",100);
ssrf审计函数:
SSRF漏洞一般位于远程图片加载与下载、图片或文章收藏功能、URL分享、通过URL在线翻译、转码等功能点处。
代码审计时需要关注的发起HTTP请求的类及函数,部分如下:
HttpURLConnection. getInputStream URLConnection. getInputStream Request.Get. execute Request.Post. execute URL.openStream ImageIO.read OkHttpClient.newCall.execute HttpClients. execute HttpClient.execute
https://www.cnblogs.com/nice0e3/p/13682434.html
https://www.cnblogs.com/nice0e3/p/13683023.html
https://xz.aliyun.com/t/7186