日志

Java 审计之SSRF篇

 来源    2020-09-17    1  

Java 审计之SSRF篇

0x00 前言

本篇文章来记录一下Java SSRF的审计学习相关内容。

0x01 SSRF漏洞详解

原理:

服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

大部分的web服务器架构中,web服务器自身都可以访问互联网和服务器所在的内网。

ssrf作用:

对外网服务器所在的内网、本地进行端口扫描,获取一些服务的banner信息 。

攻击运行在内网或者本地的应用程序。

对内网web应用进行指纹识别,通过访问默认文件实现 。

攻击内外网的web应用。sql注入、struct2、redis等。

利用file协议读取本地文件等。

php ssrf中的伪协议:

file dict sftp ldap tftp gopher

Java ssrf 中的伪协议:

file ftp mailto http https jar netdoc

0x02 SSRF产生过程

在java中ssrf会分比较多的场景,不像PHP中那样支持各种伪协议都可以去直接使用。

SSRF中内网探测

@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协议去探测该服务器内网的其他应用。

http://localhost:8080/ssrfServlet?url=http://www.baidu.com

这里用来百度来做一个演示,因为懒得自己再在内网中搭建一个环境了。

在代码中,我们未对接收过来的url进行校验,校验其url是否是白名单的url就直接进行了创建url对象进行访问和读取资源,导致了ssrf的产生。

尝试一下能不能读取文件

这里会发现根本读取不了,因为这里只支持http和https的协议。

下面来试试,在不强制转换成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();
        }
http://localhost:8080/ssrfServlet?url=file:///c:%5c%5cwindows%5c%5cwin.ini

可以成功读取到c:\windows\win.ini的文件。

SSRF中的读取文件

代码如下:

@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。

这里启动一下服务器,测试一下。

http://127.0.0.1:8080//downloadServlet?url=file:///C:%5c%5c1.txt

注意: 这里是三个斜杆,并且反斜杠需要url编码 否则就会报错

未经过url编码直接传入反斜杠

SSRF中的文件下载

漏洞代码:

@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);
            }
    }
}

输入:

http://localhost:8080/downloadServlet?url=file:///c:%5c%5c1.txt

这样就把文件给下载下来了,ssrf中的文件下载和文件读取不同点在于响应头。

response.setHeader("content-disposition", "attachment;fileName=" + filename);

这段代码,设置mime类型为文件类型,访问浏览器的时候就会被下载下来。

参考文章

https://xz.aliyun.com/t/2761#toc-1
https://xz.aliyun.com/t/206/
https://xz.aliyun.com/t/7186

0x03 结尾

SSRF的一些产生也不止文章里面写到的这么一点,包括一些第三方的组件,如果在未经过验证的情况下发起一个远程请求,那么都有可能存在SSRF漏洞。

后面打算找套源码专门审计一下SSRF。

相关文章
java – 审计和日志中的区别?
问答我经常遇到这两个词,但我没有看到很多差别在这些?我的意思是想知道他们是可互换使用还是在这两个有一些差异? 谢谢.::日志记录通常意味着记录程序运行时发生的实现级事件(调用方法,创建对象等).因此,它专 ...
1
java – 审计属性更改 – Spring MVC JPA
问答我有一个类客户端.我想能够审核这个类的属性的更改(不是整个类 – 只是它的属性). public class Client { private Long id; private String firs ...
3
手把手docker部署java应用(初级篇)
日志本篇原创发布于 Flex 的个人博客:点击跳转 前言   在没有 docker 前,项目转测试是比较麻烦的一件事.首先会化较长的时间搭建测试环境,然后在测试过程中又经常出现测试说是 bug,开发说无法 ...
1
JAVA遇见HTML——JSP篇(JSP状态管理)
日志 案例:Cookie在登录中的应用 URL编码与解码的工具类解决中文乱码的问题,这个工具类在java.net.*包里 编码:URLEncoder.encode(String s,String enc) ...
JAVA遇见HTML——JSP篇(JSP内置对象下)
日志request.getSession() 网上资料解释: request只能存在于一次访问里 session对象的作用域为一次会话 session长驻在服务器内存里,session有id标识,一个se ...
JAVA遇见HTML——JSP篇(JSP内置对象上)
日志action:表单交给哪个动作去处理 MIME类型: 浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档:因此服务器设置正确以将正确的MIME类型附加到响应对象的头部是非常重要的. 语法 ...
1
JAVA遇见HTML——JSP篇(2、JSP基础语法)
日志    <%@ page language="java" import="java.util.*" contentType="text/html ...
1
JAVA遇见HTML——Servlet篇:应用MVC架构实现项目
日志java关键字“this”只能用在方法方法体内.当一个对象创建之后,java虚拟机就会给这个对象分配一个引用自身的指针,这个指针的名字就是this.只能在非静态方法中使用 package servle ...
1
JAVA遇见HTML——Servlet篇:Servlet基础
日志  代码实现: HelloServlet package servlet; import java.io.IOException; import java.io.PrintWriter; import ...
2
从.Net到Java学习第十一篇——SpringBoot登录实现
日志从.Net到Java学习系列目录 通过前面10篇文章的学习,相信我们对SpringBoot已经有了一些了解,那么如何来验证我们的学习成果呢?当然是通过做项目来证明啦!所以从这一篇开始我将会对之前自己做 ...
1
从.Net到Java学习第三篇——spring boot+mybatis+mysql
日志从.Net到Java学习第一篇——开篇 环境:mysql5.7 新建mysql数据库demo,然后执行如下sql脚本进行数据表创建和数据初始化: -- ------------------------ ...
Java多线程系列--“基础篇”05之 线程等待与唤醒
日志概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...
1
从.Net到Java学习第十篇——Spring Boot文件上传和下载
日志从.Net到Java学习系列目录 图片上传 Spring Boot中的文件上传就是Spring MVC中的文件上传,将其集成进来了. 在模板目录创建一个新的页面 profile/uploadPage. ...
1
从.Net到Java学习第八篇——SpringBoot实现session共享和国际化
日志从.Net到Java学习系列目录 SpringBoot Session共享 修改pom.xml添加依赖 <!--spring session--> <dependency> & ...
从.Net到Java学习第七篇——SpringBoot Redis 缓存穿透
日志从.Net到Java学习系列目录 场景描述:我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回.这个时候如果我们查询的某一个数 ...
1
从.Net到Java学习第六篇——SpringBoot+mongodb&Thymeleaf&模型验证
日志SpringBoot系列目录 SpringBoot整合mongodb MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.如果你没用过Mong ...
1
从.Net到Java学习第五篇——Spring Boot &&Profile &&Swagger2
日志从.Net到Java学习系列目录 刚学java不久,我有个疑问,为何用到的各种java开源jar包许多都是阿里巴巴的开源项目,为何几乎很少见百度和腾讯?不是说好的BAT吗? Spring Boot 的 ...
1
从.Net到Java学习第四篇——spring boot+redis
日志从.Net到Java学习系列目录 “学习java已经十天,有时也怀念当初.net的经典,让这语言将你我相连,怀念你......”接上一篇,本篇使用到的框架redis.FastJSON. 环境准备 安装 ...
1
Java 数组+循环升级篇
日志数组是一个变量,存储相同数据类型的一组数据(就是能存储很多数值的数据类型) 如果说声明一个变量就是在内存空间划出一块合适的空间,那么声明一个数组就是在内存空间划出一串连续的空间. 数组的基本要求 标识 ...
1
Java多线程系列--“基础篇”04之 synchronized关键字
日志概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...
2