LOL投注平台




快速导航
KTC business

JDK反序列化Gadgets-7u21

LOL投注平台 Date:2019-12-22 01:58

从fastjson1.24版本的反序列化利用方式知道有使用jdk7u21的版本利用链,ysoserial利用工具中也有7u21利用链。现在都是7u80版本了,这个漏洞真正直接利用,估计已经很难找到了。

但是这个利用链的构造有很多之前没接触过的java特性,就此好好学习一下,也算是fastjson的前置知识吧。

先去Oracle官网下载漏洞jdk版本7u21,漏洞影响7u25之前的版本,整条链poc貌似只适用于7u21以前。

我们从ysoserial源码中抠出7u21的利用代码来分析,具体代码由于比较长,不全部在此贴出,只截取需要的部分,所有代码已上传github。

从调用栈中,可见最后是obj.newInstance(obj是虚指)触发poc执行恶意代码,调用栈再往下之后就是java class类的newInsatance内部实现了,不细纠。

newinstance实例化会默认触发执行static方法,构造方法代码,如下:

所以我们的payload需要放在最后执行的恶意类的static或构造方法中。知道这点后,我们从头开始慢慢寻找其他需要条件。

其中的限制条件4 _tfactory 这个参数是有说法的,在其他人博客中有存在对于 _tfactory 的参数的说明:

但其实这跟jdk版本是有关的,1.7下不同的jdk版本这段代码是不同的。

细心的同学可以注意到上面jdk1.7u80两个弹框成功不成功的下方都会null指针报错。

但是1.7u21也可以兼容给_tfactory赋值的情况,所以还是给 _tfactory 赋值比较好,可以兼容不同的版本。

至此总结我们构筑一个恶意的TemplatesImpl类,在调用这个恶意类的getOutputProperties方法时,需要满足的限制条件。即,构筑恶意TemplatesImpl类的需要条件。

6.我们需要执行的恶意代码写在_bytecodes 变量对应的类的静态方法或构造方法中。

在分析完第二句触发漏洞的语句后。回来看第一句构筑。由于需要动态对于类结构进行操作,有使用到Javassist包

可以看到上面的Gadgets类完美符合了我们之前在利用过程中提到的全部需要条件。但是Gadgets构造的恶意TemplatesImpl类比起我们需要的POC条件多1处东西:

我始终没有找到这个到底有啥用,去掉后实验,没有任何影响。如果有老哥知道,可以联系我,非常感谢。

以上需要注意一个情况,我们的恶意字节码类lala类,使用了static修饰符。其实我们payload写在构造函数中是可以不使用static修饰符不会影响。

但是如果我们想把payload写在static初始化块中,类就需要使用static修饰符时。不然最后实例化是不会成功的。

就相当于是以下的情况,内部类是不允许存在static修饰符的,原理可以参考。

ps.突然发现非static方法块也是可以写payload…..但是不纠结这个了!!

目前的结论已经可以移花接木到fastjson的利用链中形成一套完成利用链。以及其他很多组件的利用链的最后一步都是TemplatesImpl类(限于jdk1.7版本,1.8会编译错误,原因未知)。

但是就单独作为一条利用链来说,只有exp触发点和一点点长度的利用链是不够的,我们需要继续延伸到一个反序列化readObject点,使服务端一触发反序列化,就可以沿着利用链到exp触发点。

动态代理是java的特性之一,其实就可以理解为web应用中的,在执行正式代码之前先过一个函数(比如spring的AOP)。但是以上类比只是为了便于理解,实际上spring的AOP之类的反而是基于java的动态代理实现的。

下面将举例动态代理SubjectImpl类,即在SubjectImple类前面建立一个。

2.interfaces,被代理类所实现的接口,这个接口可以是多个。(即需要拦截的接口)

之后只要我们调用了返回之后的对象中被安排了代理的接口,就会进入invocationHandler的invoke函数。

5.调用这个返回对象的被代理接口即可。(此处注意这个返回的对象不是只有被代理的接口类中的接口,还有一些常用接口,之后会截图说明。)

其实就是因为AnnotationInvocationHandler类其实是一个InvocationHandler接口的实现类。它不只是在cc的利用链中作为反序列化点,还是作为动态代理的实现函数(有一个自己的invoke方法)

equals方法会根据this.type类中的方法去遍历调用传入对象中的所有对应的方法。那么!

3.调用这个代理类的equals方法,同时給入恶意实例,就会遍历this.type这个类中的方法对恶意实例中的对应方法进行调用。唯一的缺点就是调用的方法不能传入参数。(因为var5.invoke(var1);只传入了对象,没有传入参数)

再是this.type=Templates.class,因为TemplatesImpl继承自Templates接口,并且它有我们要的方法,并且在第一个(为啥需要恰好又刚好在第一个,之后有说法)。

为啥this.type需要选用类中第一个方法是我们需要调用的方法的类呢?

因为不是的话,就需要考虑更多,比如报错退出。可以看到在执行完我们的payload后是会报错退出的,当然这对我们paylaod的执行没有影响。

但是假如我们需要调用的方法不在第一个,而前面是一个需要参数的方法,就会因为没有传入参数而报错退出。(比如我们把Templates.class改成TemplatesImpl.class)

如果我们需要调用的方法前面有一些其他方法,但是都是不需要参数的,我们还需要构造this.memberValues,让前面这些函数的返回值与this.menberValues里面一致才不会返回false退出。就会有一串的麻烦(目前来看这样也是可行的,但是假如这里真的改了this.memberValues之后LinkedHashSet那关就过不去了!实际上我们只能且必须要找到一个第一个方法是能够代码执行的方法!)

所幸我们可以找到一个Templates类,它进行代码执行的方法是第一个,万幸。

LinkedHashSet类继承自Hashset,具有Hashset的全部特点:元素不重复,快速查找,快速插入。新增的特性是有序,数据结构上使用双向链表实现。(之所以用LinkedHashSet就是因为其有序的特性,后面会说到为什么需要有序)

具体是如何实现这个集合的,我们就不纠结了。我们需要通过LinkedHashSet连接writeObject序列化与readObject反序列化这个利用链入口至**a.equals(b)**这个我们之前得到的触发点。

可以看到key.equals(k)符合我们前面说到的a.equals(b)的格式。在只有两个元素的情况下,k为有序集合中第一个元素,key为第二个元素。

这也就是为什么需要有序集合的原因,如果是普通集合,不会一定会符合这个a.equals(b)的顺序

2.(k = e.key) != key :templates(就是k) != proxy(就是key)(我们需要左边这个表达式不满足,才会执行右边的漏洞触发函数key.equals(k)。这是的特性,执行到一个为true的,后面的表达式就不执行了)

因为templates和proxy完全是两个不同的对象。所以第二个条件满足。

但是第一个条件需要hash相同,如果不是偷看答案的小白(我自己)肯定会突然僵住,特么这咋可能hash相等,当场直接gg。实际上套路还是很深。看hash是如何生成的

以上代码还会有最后一个疑问,为啥我们填入this.memberValues的map要先试用override字符串来占位,直接填入恶意的攻击类templates不行么?

这里调用了我们触发漏洞的函数map.put(),同时也是按照我们的漏洞触发顺序去调用map.put,这会导致payload会在我们本地触发,之后会无法序列化成功(至于为啥序列化不成功不想追究了!)

这也就是为什么之前的payload说到在高版本创建需要使用反射把恶意的this.type写进去,当然构造时可以这样,触发时就必须走构造函数,骚操作不了了。

再是通过LinkedHashSet类,左打通了序列化与反序列化的入口点,右在反序列化恢复集合的过程中存在着一处a.equals(b)可以连接proxy.equals(templates)这一触发点。

虽然说这条利用链已经被封了好久了,但是我们也可以意识到被封杀的是AnnotationInvocationHandler构造方法处。

封面号文章仅代表作者本人观点,不代表封面号平台的观点,与封面号立场无关,文责作者自负。如因文章内容、版权等问题,请联系封面新闻。



相关阅读:LOL投注平台

 l