0x00 前言

反序列化总纲

主要是依赖Java代码审计——fastjson 1.2.68 反序列化漏洞 AutoCloseable

看了这个poc之后,就一直纠结,为啥只有openjdk >= 11才可以用,最后在调试+看了大部分文章之后才终于明白

0x01 环境

这里需要准备两个jdk,一个 1.8 一个11

0x02 环境约束

首先先来看一下Fastjson的构造参数选择:

通过createJavaBeanDeserializer进来

fastjson maven 最高版本_json

走到JavaBeanInfo.build

fastjson maven 最高版本_fastjson maven 最高版本_02

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

fastjson maven 最高版本_Java代码审计_03

获取构造参数

fastjson maven 最高版本_java_04

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

fastjson maven 最高版本_赋值_05

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

javap -l java.io.FileOutputStream

fastjson maven 最高版本_json_06

又找了一下相关资料

LocalVariableTable该属性的作用是描述帧栈中局部变量与源码中定义的变量之间的关系。可以使用 -g:none 或 -g:vars来取消或生成这项信息,如果没有生成这项信息,那么当别人引用这个方法时,将无法获取到参数名称,取而代之的是arg0, arg1这样的占位符。start 表示该局部变量在哪一行开始可见,length表示可见行数,Slot代表所在帧栈位置,Name是变量名称,然后是类型签名。

简而言之,因为 1.8 没有LocalVariableTable 所以fastjson拿不到。其实可以剥一下代码把获取LocalVariableTable 的方式拨出来自己用。

0x03 调用链

先上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
}
1.java.util.zip.InflaterOutputStream

java.util.zip.InflaterOutputStream类实现了一个输出流过滤器,用于解压缩以“deflate”压缩算法存储的数据。最典型的就是gzip

首先来看调用的构造方法,三个参数 out infl protocolVersion。

fastjson maven 最高版本_Java代码审计_07

这里的infl就是存储的数据对象,out就是输出流。

out

这里的out流采用了java.io.FileOutputStream的方式进行获取,

fastjson maven 最高版本_赋值_08

之后FileOutputStream会打开一个目标文件的输出流

infl

那么问题来了,为什么infl在赋值的时候需要用如下格式进行赋值呢:

"infl": {
           "input": {
               "array": "eJxLLE5JTCkGAAh5AnE=",
               "limit": 14
           }
        },

绕了好几圈,最后才明白,原来在fastjson在解析infl找到infl的类是ByteBuffer,那么就会去调用自己的fastjson-1.2.58.jar!\com\alibaba\fastjson\serializer\ByteBufferCodec.class,也就是这里

fastjson maven 最高版本_fastjson maven 最高版本_09

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

fastjson maven 最高版本_Java代码审计_10

那么现在的情况就是获取到了hb和limit的值

write

最终需要触发的是write方法

fastjson maven 最高版本_赋值_11

这里调用inflate对buf进行解压

fastjson maven 最高版本_java_12

然后通过write进行写入。

2.sun.rmi.server.MarshalOutputStream

先来看sun.rmi.server.MarshalOutputStream,按照fastjson来说没有无参构造方法就会调用最多的

fastjson maven 最高版本_赋值_13

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

fastjson maven 最高版本_json_14

拿到两个参数名out和protocolVersion

这里的sun.rmi.server.MarshalOutputStream主要作用是用来触发write

fastjson maven 最高版本_fastjson maven 最高版本_15

这里触发bout.setBlockDataMode(true);

fastjson maven 最高版本_赋值_16

触发drain()

fastjson maven 最高版本_java_17

触发write

fastjson maven 最高版本_赋值_18