java反序列化漏洞复现

JAVA反序列化分析思路

从危险函数出发开始寻找不同类的同名函数实现任意方法调用(反射/动态加载字节码)

反射基础

r是一个Runtime实例,c是一个Runtime类,对c使用反射(getMethod)来获取exec方法,然后在r上调用这个方法

漏洞复现

CC1

1
public class ApacheSerialize {    public static void main(String[] args) throws Exception {        Transformer[] transformers = new Transformer[] {                new ConstantTransformer(Runtime.class),                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),                new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"})        }; //将transformers数组存入ChaniedTransformer这个继承类    Transformer transformerChain = new ChainedTransformer(transformers);java

transformer

InvokerTransformer(反射利用点)

首先是由InvokerTransformer类中的transform实现了任意方法调用的同时还在构造函数中导致参数可控

上面的java反射就相当于

(实际上自己构造的时候在IDE中直接看需要填入的变量还是挺方便的,这里不太好看懂)

如以下语句就可以实现命令执行

将Runtime实例传入transfrom,cls就获取了Runtime类,method获取了Runtime类中的exec方法,然后对Runtime实例使用传入的传输calc

poc如下

最后的目标是回到readObject,所以此时要寻找不同的方法来调用transform


MAP

在TransformedMap类里面可以找到调用了transfrom方法的位置

而valueTransformer是TransformedMap构造函数中的一部分,也就是可控

如下构造就可以对invokerTransformer调用tansform方法(由于构造函数是保护的,这里用到decorate装饰器)

继续向上寻找发现是TransformedMap的父类的MapEntry类重写了setValue方法

则可以如下,构造一个TransformedMap并且调用setValue即可(让TransformedMap的value为构造的invokertransformer然后通过setValue调用transform)

现在则需要一个可以遍历数组的地方并且需要value可控或者不同名的调用了setValue


AnnotationInvocationHandler

pop链的最后一步

满足两个if条件后即可调用setvalue

但是这个类是一个default类,需要用反射创建,并且可以看见他的构造函数中的第一个参数是一个注解类(如Override)

此时有三个问题:

1.如何通过if判断来进入这个setvalue中

2.setValue貌似不可控

3.Runtime对象本身是不可序列化的,需要通过反射


Runtime实现反射调用

解决Runtime不可序列化的问题
正常情况,由于Runtime对象构造函数是私有的,需要通过getRuntime方法来获取这个对象

上图,通过.class获取了Runtime类,然后getMethod反射获取getRuntime方法,然后调用这个方法,获取r这个Runtime实例化对象,最后反射获取exec方法并且在r上调用

转换为InvokerTransform类中则是

chainedtransformer

这时候通过chainedtransform实现循环调用来使得调用更加简洁


进入if语句

1.通过getKey来获取传入的memberValue中的键是否存在于menberTypes中

如上,反序列化入口获取了type参数也就是下面的Override.class

接着获取了这个类中的成员方法,检查poc中传入的键在这个类中的成员方法终会是否存在

则传入一个有成员方法的类并且将key改成他的一个方法名

如Target的value方法截图

如上修改即可

2.

即检查是否可以强转,这里不需要绕过


实现可控SetValue

正常而言,这里会调用

也就是这里的transform的value不可控

也就相当于要把下面的语句换成上面的语句(实现value可控)

ConstantTransformer

其transformer方法直接返回自身的一个变量同时可控
就可以利用它通过Runtime.getRuntime()创建一个Runtime实例

此时即可转换成poc中的


pop链回溯

通过反射实例化了AnnotationInvocationHandler类—>对他的value遍历并且调用setvalue,调用TransformedMap的父类的MapEntry#SetValue–>transform方法(这一步为了实现可控SetValue借助到ConstantTransformer类)—>在value中,我们恶意构造的chainTransformer类实现命令执行

总结

第一次看java的链子,看了点反射跟基本语法就强行看完了,感觉看懂了但是让自己复现还是很困难(语法不清晰),写的逻辑不太清晰

注意:以上的链子是利用TransformedMap回调调用+AnnotationInvocationHandler类调用SetValue来到transfrom方法,YSO链子的Lazymap需要利用动态代理

本质上TransformedMap和Lazymap都只是一个装饰器,TransformedMap是回调调用(新加入一个键值对的时候调用transform) LazyMap是懒加载(get找不到值的时候去触发transform)

CC6

为了解决sun.reflect.annotation.AnnotationInvocationHandler#readObject在8u71后的变化(不再直接调用自定义的map而是弄了一个LinkedHashMap)

Map

首先看一下LazyMap,他的get方法调用了transform

又回到了如何调用LazyMap#get(cc1demo利用的是Hashmap,yso的链子用的是动态代理调用lazymap)的问题,这里利用了TiedMapEntry#getValue,

hashCode方法调用了getValue

hashmap的hash方法调用了hashcode

他的readObject方法又调用了hash方法

则得到链子

1
Hashmap#readObject-->Hashmap#hash(key)-->TiedMapEntry#hashcode-->TiedMapEntry#getValue(key)-->LazyMap#get

同样写一个chain,decorate绑定到outputmap上面

再把这个恶意的map绑定到TME的key上,最后构造一个hashmap,将TME放到他的key上

问题

调试发现,反序列化的时候并没有进入tranfrom方法,在map中判断存在这个叫做keykey的key

这是因为hashmap#put的时候触发了对TME的hash操作

所以最后添加上remove操作

总结

跟cc1很像的链子,主要是这次分析换成了LazyMap,绕过了jdk8u71

CC3

链子

利用字节码打的cc链

首先记住 可以通过newTransformer和getOutputProperties来RCE

cc3的关键在于TrAXFilter类的构造方法调用了newTransformer来绕过InvokerTransformer的限制

至于如何调用这个类的构造方法,利用类InstantiateTransformer

字节码

需要保证name为空 给class赋值

bytecodes不为空,同时对于_tfactory需要存在调用getExternalExtensionsMap的时候不返回空

因为他是TransformerFactoryImpl类,给他赋一个即可

对于TrAXFilter的赋值

需要保证以下调用链的正常

写一个反射修改属性的函数

构造合法的恶意类

首先需要继承AbstractTranslet

注意name和tfactory

总结

链子剩余的部分和cc1 cc6相同,主要是会写恶意字节码类


java反序列化漏洞复现
http://example.com/2023/09/24/java反序列化漏洞复现/
作者
z2zQAQ
发布于
2023年9月24日
许可协议