0x00 前言
组长讲得还是那么好,但是这条链有点长,其中的知识有点多,所以我写得会多一点。
cc3的优点在于没有用到InvokeTransform类,相当于多了一种攻击手段。
0x01 思路
- 代码执行方式
从某种程度上说,Java反序列化的目的是实现任意代码执行。
但是开发者往往不会在程序里留下常见的危险方法,这就需要我们动态调用危险方法,比如通过Java反射机制或者静态代码块执行/构造函数执行。
在前面所学的cc1、cc6等链子中,我们采用的是Java反射机制(主要是InvokeTransform类),本文所学的cc3链将采用静态代码块执行(构造函数执行也是一样的),并运用到cc1、cc6等链子中。
- 类初始化
静态代码块执行的条件是所在类进行初始化,也就是newInstance。想要执行一个类的静态代码块,就需要想办法加载这个类并且初始化。
所以寻找底部链子的思路就是:
找到可以执行任意类加载并且将其初始化的方法,然后调用上述方法。
当底部链子找到以后,再一层层往上找,直至找到readObject方法。
0x02 cc3
底部链子
类加载 -> 类初始化 -> 静态代码块调用
ClassLoader.defineClass()
静态代码块所在类是用户自定义的类,我们的目的是加载这个类
跟类加载相关的类是ClassLoader类,该类进行类加载的核心方法是loadClass方法。loadClass方法先按照双亲委派机制向父类加载器请求加载目标类;如果父类加载器无法加载目标类,则使用findClass方法,而findClass方法又会调用defineClass方法。defineClass方法的作用是将字节码定义为一个Class对象,符合我们的目的。
但是defineClass方法被重写了很多种:
查找用法,希望找到一个类可以调用defineClass方法:(这里直接给出)
TemplatesImpl.TransletClassLoader.defineClass()
跟进该方法:
该方法是default类型,查找用法:
父类的defineTransletClasses方法是private类型,再查找用法:
前两个方法只是单纯的类加载,并没有初始化;第三个方法则进行了初始化:
该方法是私有的,查找调用:
终于遇到public类型的方法了!先尝试写个poc:
这里不妨思考一下_name
、_bytecodes
、_tfactory
这三个属性的作用,或者说为什么要反射修改它们。
_tfactory
属性不用修改也可以反序列化弹calc,这里是为了更好地展示poc,无伤大雅。
至此,我们的链子尾部TemplatesImpl类搞定了,下一步是一步步往上调用直至readObject方法。
但是在下一步之前,想必读者并不能成功弹calc,且看下文:
静态代码块
箭头所指的地方自然有其用处,如果不继承AbstractTranslet类,程序会报空指针错误,具体的细节如下:
这里最好跟着组长调试,直接给出问题:
满足第一个箭头所指的if条件即可,上文已经给出答案了。
往上调用
其实到了这一步已经可以采用反射了,可以通过cc1的InvokeTransform类甚至往上的整条链来构造cc3链。
但是我们的目的就是不想使用InvokeTransform类,这里就不赘叙上述简单的拼接了。
TrAXFilter
上文所讲的TemplatesImpl.newTransformer方法可以被其他类调用,那就查找用法:
锁定TrAXFilter类,调用如下:
这个类不能序列化,那么这个链节点就不能被序列化保存,进而无法进行反序列化。但是有这么一个类及其方法,可以接受参数并调用该参数的构造方法。
细品,序列化保存的数据是静态的数据,但若把那些不可序列化的数据以动态的形式调用,照样可以保持链子不崩坏。
这个类就是InstantiateTransformer类。既然学到这里了,给出一个类想必读者可以自行写出当前的poc:
都transform方法了,下一步的poc直接用cc1的(cc6的也可以),完整的poc给出:
0x04 结语
想必读者已经深有感悟,Java反序列化的链子各个部分还是比较独立的(起码在cc链是这样的)。