细节的思考
1、首先是关于 parse和 parseObject,二者都可以将 JSON字符串反序列化为 Java对象,不同的代码可能会采用不同的反序列化方法。
加入要反序列化的 jsonString如下所示:
1 | { |
三种反序列化方式对应三种不同的结果:
(1) JSON.parse(jsonString)
当JSON字符串包含@type
属性时,FastJSON会尝试实例化指定类。
调用顺序:
调用无参构造方法创建对象实例。
调用setter方法:根据JSON键名匹配目标类的 setter()
方法(若存在)。
直接赋值字段:若没有setter方法,则直接通过反射修改字段(需字段为 public
或启用Feature.SupportNonPublicField
)。
触发getter方法:调用某些特殊的符合条件的 getter()
,如果JSON中存在某些特殊逻辑(如 JSONObject
嵌套),可能在反序列化过程中意外触发 getter()
。
(2) JSON.parseObject(jsonString)
包含 @type时与 parse()行为一致,会实例化指定类并调用setter/字段。
额外调用 getter
:
由于parseObject()
会尝试将结果转为 JSONObject,会自动调用目标对象的所有 getter
方法以获取属性值。
这会导致 getter
方法中的逻辑被触发 (即使JSON中未显式包含对应键)。
(3) JSON.parseObject(jsonString, Target.class)
直接解析 JSON字符串到指定类 Target.class
,忽略 @type
属。
后续逻辑与 JSON.parse()
相似,只不过是在 Target.class
中进行调用罢了,若 Target.class
无危险逻辑,则不会出现安全漏洞。
parse调用 getter()绕过
Fastjson <= 1.2.36
反序列化时首先得到一个JSONObject对象,然后将该JSONObject对象置于”JSON Key”的位置。Fastjson在反序列化时会对”JSON Key”自动调用JSON.toString()。JSONObject是Map的子类,执行toString()时会将当前对象转为字符串形式,会提取类中所有Field,自然会执行相应的getter、is等方法 – Kingx师傅的解释,在这里直接引用了。
1 | { |
1 | public class Parent { |
为什么 Fastjson > 1.2.36不能利用这种绕过调用 getter了 (参考 jlkl师傅)
Fastjson >= 1.2.36
1 | [ |
Fastjson不出网利用
TemplatesImple链
TemplatesImpl链在之前提到过,利用字节码加载恶意类RCE,由于 private属性的限制需要开启 Feature.SupportNonPublicField
,很鸡肋。
BasicDataSource链
依赖:Tomcat数据库驱动组件 tomcat-dbcp
调用链 - org.apache.tomcat.dbcp.dbcp2.BasicDataSource#getConnection()
–> createDataSource()
–> createConnectionFactory()
先给出 EXP
JSON.parse(jsonString) Fastjson <= 1.2.36
1 | { |
JSON.parse(jsonString) Fastjson >= 1.2.36
1 | [ |
JSON.parseObject(jsonString)
1 | { |
首先通过触发 getConnection()方法调用 createDataSource()方法
createDataSource()方法调用 createConnectionFactory()方法
createConnectionFactory()方法调用 DriverFactory.createDriver()方法
跟进,来到 sink点Class.forName(driverClassName, true, driverClassLoader);
根据 P神《Java安全漫谈》第一篇文章的内容可知,当使用 Class.forName()时,第二个参数 initial为 true时,类加载后将会直接执行 static{}静态代码块中的代码
其中 driverClassLoader和 driverClassName可控 (调用 setter),接下来寻找一个可以利用的恶意类即可
需要额外关注的是 driverClassLoader,即 exp中的 com.sun.org.apache.bcel.internal.util.ClassLoader
1 | protected Class loadClass(String class_name, boolean resolve) throws ClassNotFoundException { |
如果 class_name以 $$BCEL$$开头,则取 $$BCEL$$之后的部分解码后作为 class的字节码,并调用 defineClass获取 class对象。
这条链子到这里就齐了,在恶意类静态代码块中写恶意代码,构造 $$BCEL$$恶意字节码通过 fastjson赋值给 driverClassName,并将类加载器 com.sun.org.apache.bcel.internal.util.ClassLoader
赋值给driverClassLoader,就可以在不出网反连的条件下执行恶意代码了。
版本的局限性
1、绿盟官方对于 BasicDataSource链在 Fastjson各版本的利用进行了测试
https://blog.nsfocus.net/fastjson-basicdatasource-attack-chain-0521/
BasicDataSource链不出网利用只适用于 Fastjson <= 1.2.24,因为从 Fastjson >= 1.2.25开始,checkAutoType中对 BasicDataSource进行了单独的检测,无法绕过
L … ; 只能帮助绕过黑名单限制
2、BasicDataSource类在不同版本的 tomcat-dbcp包中有不同的位置,参考 KINGX师傅的 blog
参考文章
https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html – P神对于 $$BCEL$$的解释
https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html – KINGX
https://blog.nsfocus.net/fastjson-basicdatasource-attack-chain-0521/ – 绿盟
https://blog.csdn.net/solitudi/article/details/120275526 – Y4tacker, $ref调用 getter
https://mp.weixin.qq.com/s/C1Eo9wst9vAvF1jvoteFoA – KINGX, FastJson反序列化漏洞利用的三个细节