Apache Commons Collections主要提供了两个类,TransformedMap和LazyMap类,其可以修饰一个Map数据,当对该Map数据进行具体操作时就会触发transform过程。Apache Commons Collections反序列化的CC链主要使用的是TransformedMap类,而Ysoserial CC1链主要使用的是LazyMap类。
TransoformedMap类的关键点在checkSetValue()方法,我们构造好的含有利用代码的ChainedTransformer利用链即transformers数组会循环进入此处。
而Ysoserial CC1链使用的LazyMap类关键点在其get( )方法,会触发transform过程。
在不看CC1链具体代码之前,我们不妨先尝试构造一下这个利用链。
要想触发transform过程,此时this.factory的值就需为我们构造好的利用代码即ChainedTransformer利用链,当调用LazyMap对象的get方法时就会触发命令执行。
那this.factory参数是否是可控的?
LazyMap的构造函数是受保护protected的,只有他自身或者继承他的类可以用,我们不能通过构造函数传参。但其有一个decorate()方法,其参数2为Transformer类型,因此可以利用这个方法创建一个LazyMap对象,相关代码如下:
但要利用此漏洞,就需要通过网络传输payload,在服务端对我们传过去的payload进行反序列时执行代码,而该POC的关键依赖于调用lazyMap.get()方法,而这完全不可控。
因此就需要寻找一个特定的可序列化类,该类重写了readObject( )方法,并且在readObject( )中调用了lazyMap的get()方法。需要注意的是,在java中如果重写了某个类的方法,就会优先调用经过修改后的方法。
Ysoserial CC1链中利用的还是在上篇文章中分析过的AnnotationInvocationHandler类,该类重写了readObject( )方法,但其并没有明确的调用get()方法,此时就使用到了动态代理。
AnnotationInvocationHandler类实现了InvocationHandler接口,其invoke( )方法,代码如下:

如上代码所示,其主要作用是当代理的对象调用toString,hashCode,annotationType时,就返回相应的结果,如果调用了其他方法,就会去执行Object var6= this.memberValues.get(var4)。
那如果this.memberValues是可控的,且其为Map类型,就可以达到我们的目的,查看其构造函数,this.memberValues正好是Map类型。

目前只要可以调用AnnotationInvocationHandler实例对象的invoke方法就可以触发攻击链,导致代码执行。
前文提到每一个proxy代理实例都有一个关联的调用处理程序InvocationHandler,而invoke方法就是代理对象调用方法时的调用处理程序。
因此我们需要构造一个动态代理的对象,让其调用方法,从而触发invoke方法。
接着看AnnotationInvocationHandler的readObject方法。

this.memberValues是可控的,当其是一个AnnotationInvocationHandler类生成的动态代理对象时,在调用entrySet()方法时,会自动去调用invoke方法,而由于其调用的不是toString,hashCode等方法,就会执行Object var6 = this.memberValues.get(var4),从而完成攻击链条,导致代码执行。
攻击链条:
反序列化数据流 执行redObject()方法 执行this.memberValues.entrySet()方法 触发InvocationHandler的invoke方法 执行invoke方法里的this.memberValues.get() 调用LayzMap的get()方法 执行get方法里的this.factory.transformer() 调用chainedTransformer的transform方法 循环调用InvokerTransformer的transform方法构造Runtime对象 执行Runtime对象的exec方法