fastjson原生反序列化链分析
用户9528 WEB安全 232浏览 · 2025-03-04 09:50

前言 FastJson原生反序列化链不同于之前分析fastjson利用链,之前利用链分析的是fastjson在解析json格式的数据时,通过构造恶意的json数据,期间会涉及到1.2.24-1.2.80等不同版本的绕过以及额外依赖。而这里的FastJson原生反序列化链是利用FasJson当中函数的调用关系,结合java原生反序列化来对目标应用进行攻击 主要还是利用BadAttributeValueExpException对象通过它的readObject方法调用ToStringBean的toString方法,val字段的值是一个JSONArray对象,所以会调用JSONArray的toString方法。但是由于JSONArray本身并没有toString方法,这里会直接调用JSON的ToString方法 在JSON的ToString会调用自身的toJSONString方法,而toJSONString方法能够调用任意类的getter方法,从而实现了template对象getOutputProperties方法的调用, 分析 为了更好理解反序列化调用,这里自定义类A,其中getter方法中执行代码,

直接在A中下断点执行,payload如下:

调用栈如下:

可以看到调用栈

展示了 Java 反序列化过程中从反射机制到实际对象读取的调用链,涉及到反射方法调用、序列化类的反序列化逻辑以及从输入流中恢复对象的过程。 这一过程在下面有多处涉及,就不多说了

注意到栈顶的下一个栈帧调用了ASMSerializer_1_A.write方法,在idea中无法调试显示,为了直观展示出调用A.getName的逻辑,考虑通过工具从jvm内存中将已经生成并加载的ASMSerializer_1字节码文件给dump下来,
Pasted image 20250302193301.png
图片加载失败
看到这里调用了a.getName()

按照调用关系,hashmap中,key作为任意对象应该不影响作为value的BadAttributeValueExpException反序列化调用,但是当将key设置为空时,运行发现并没有执行代码,经过反复调试,发现从BadAttributeValueExpException.readobject方法报错,这里跟进调试 java.io.ObjectInputStream#readFields
Pasted image 20250302191159.png
图片加载失败
java.io.ObjectInputStream.GetFieldImpl#readFields
Pasted image 20250302191216.png
图片加载失败
从上面这里读取了属性值,接着还是通过反序列化方法java.io.ObjectInputStream#readObject0递归调用
Pasted image 20250302225546.png
图片加载失败
Pasted image 20250303170355.png
图片加载失败
Pasted image 20250302225644.png
图片加载失败
从上面可以看到,因为BadAttributeValueExpException对象中val属性值为jsonArray实例,所以这里的类描述信息就是com.alibaba.fastjson.JSONArray 接着执行到java.io.ObjectInputStream#readSerialData
Pasted image 20250303170617.png
图片加载失败
到这里开始就是文中开头提到的Java 反序列化过程中从反射机制到实际对象读取的调用链,
Pasted image 20250303170651.png
图片加载失败
最后调用的是com.alibaba.fastjson.JSONArray#readObject方法
Pasted image 20250303171127.png
图片加载失败
跟进java.io.ObjectInputStream#defaultReadObject,
Pasted image 20250303171522.png
图片加载失败
内部又调用了readobject0方法
Pasted image 20250303171552.png
图片加载失败
接着又进行反射读取反序列对象流程,一直到java.util.ArrayList#readObject中
Pasted image 20250303171752.png
图片加载失败


从上面payload构造也好理解,jsonArray实例中存在arraylist对象, 重点分析下从java.io.ObjectInputStream#readObject进入的流程,在java.io.ObjectInputStream#readObject0中,会根据读取的序列化流的字节信息去分别调用不同的方法,
Pasted image 20250303172157.png
图片加载失败
这里因为是object对象,所以进入的java.io.ObjectInputStream#readOrdinaryObject方法,这里java.io.ObjectInputStream#readClassDesc是读取类的描述符信息,跟进
Pasted image 20250303172743.png
图片加载失败
可以看到,它会根据不同的类型调用不同的方法,这里调用的是java.io.ObjectInputStream#readNonProxyDesc
Pasted image 20250303173428.png
图片加载失败
在java.io.ObjectInputStream#readNonProxyDesc中,又会调 com.alibaba.fastjson.JSONObject.SecureObjectInputStream#resolveClass,接着通过checkAutoType去进行检查是否能够调用
Pasted image 20250303173706.png
图片加载失败
在这里导致程序无法执行下去

为了不让程序执行到这里,注意检查java.io.ObjectInputStream#readObject0方法,
Pasted image 20250303174712.png
图片加载失败
发现java.io.ObjectStreamConstants#TC_REFERENCE注释中可以看出,进入该条件的方法就是,这个对象已经被写入到序列化流中,
Pasted image 20250303174255.png
图片加载失败
也就是说在尝试反序列化A类前,可以将A提前写入到序列化流中,这样就会进入该方法,
Pasted image 20250303174802.png
图片加载失败
从而绕过检查, 那么这里payload就是

a在badAttributeValueExpException前执行,

通过对整个流程梳理,还有一点疑问就是,在a反序列化时,为什么可以通过检查,按道理说,A类也要进过checkAutoType检查报错,但实际上,作为A类(没有被JSONArray嵌套),它调用的是java.io.ObjectInputStream#resolveClass
Pasted image 20250303175421.png
图片加载失败
而被JSONArray嵌套的类,也就是paylaod中这样的形式,

实际上是会调用com.alibaba.fastjson.JSONObject.SecureObjectInputStream#resolveClass,原因很简单,就是在调用com.alibaba.fastjson.JSONArray#readObject时,fastjson将ObjectInputStream封装成了SecureObjectInputStream,进而可以自定义检查规则。
Pasted image 20250303175744.png
图片加载失败


总结 首先是被嵌套在JSONArray里面的template对象,由于JSONObject#resolve()无法正常解析B类型的缘故,所以造成payload2无法正常执行,其次是前一个template对象在ObjectInputStream#resolveClass的作用下成功解析,并协助后面的JSONArray里面的template绕过了审查。

0 条评论
某人
表情
可输入 255
目录