Analysis of commons-collections 1 (CC1)
jdk-7u21 ysoserial IDEA
参考
利用链
transform:124, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:151, LazyMap (org.apache.commons.collections.map)
invoke:69, AnnotationInvocationHandler (sun.reflect.annotation)
entrySet:-1, $Proxy0 (com.sun.proxy)
readObject:346, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadObject:1004, ObjectStreamClass (java.io)
readSerialData:1891, ObjectInputStream (java.io)
readOrdinaryObject:1796, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
deserialize:27, Deserializer (ysoserial)
deserialize:22, Deserializer (ysoserial)
run:38, PayloadRunner (ysoserial.payloads.util)
main:81, CommonsCollections1 (ysoserial.payloads)
不看POC看利用链
yso
入口deserialize:27, Deserializer (ysoserial)
这里开始反序列化
常规进入几个readobject
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
在readObject0:1348, ObjectInputStream (java.io)
进入 readOrdinaryObject
方法 到
readOrdinaryObject:1796, ObjectInputStream (java.io)
从这里可以看到obj
进来了,是一个AnnotationInvocationHandler@737
对象
再到
readSerialData:1891, ObjectInputStream(java.io)
到
invokeReadObject:1004, ObjectStreamClass (java.io)
在此处(invokeReadObject:1004, ObjectStreamClass)
进入invoke
方法
详细参数如下
上面可以看到是Method的invoke
,所以下一步走到
invoke:601, Method (java.lang.reflect)
在601进入下一个invoke
,obj
依旧是 AnnotationInvocationHandler@737
详细参数如下
到
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
。。。(中间省略)
到了
readObject:346, AnnotationInvocationHandler (sun.reflect.annotation)
这里开始离开jdk,来到CC
的内容
终于来到AnnotationInvocationHandler
,可以看到这里的this就是AnnotationInvocationHandler@737对象
然后到同文件的
invoke:69, AnnotationInvocationHandler (sun.reflect.annotation)
来看一下进入下一个位置相关的参数
疑惑:这里的this.memberValues
为什么是LazyMap
,不是很懂(看到后面代理的部分就懂了)
然后带参数var4 = entrySet
进入LazyMap
的get方法
接着到
get:151, LazyMap (org.apache.commons.collections.map)
可以看到已经到了熟悉的地方了
最后再到
transform:124, InvokerTransformer (org.apache.commons.collections.functors)
此处小总结一下:
中间有些没有反编译出来,导致有些参数不知道是怎么进去的。
CC部分:CC中的三个Transformer的实现类
每一个都专门看一下他们的transform方法
1# InvokerTransformer(作用:反射)
首先要了解反射
反射
反射其实就这个,需要三个东西,Method,obj和arg
看看代码,其中transform方法
如下
关键代码只有三行
用这个三个参数的构造方法实例化,就可以控制到这三个参数了
就是基本的反射,我们修改一下参数就可以反射命令执行了
这也就意味着,进入了这个地方就可以做反射,那么这里的危害不一定是命令执行,但是危害在可以利用反射获得任意对象的方法。这里也为这个CC1漏洞利用链打下一个基础。
简单总结一下就是,通过传入三个参数,就可以做到反射。
2# ConstantTransformer
看一下具体代码
传入了什么input
,都会返回一个iConstant
3# ChainedTransformer
看代码
iTransformers
是一个Transformer数组
这个transform方法
就是遍历数组里面的Transformer
,并且调用他们的transform方法
3个都是返回对象。
利用三个实现类进行RCE
前面说到1# InvokerTransformer的时候,反射明明可以做出RCE
了,那么为什么不直接使用这个点呢。
因为我们还要看上面的这三个参数是否可以被我们控制,但是现在可以结合利用上面三个实现类,进行RCE
。
POC:
package ysoserial.payloads; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.python.antlr.ast.Str; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { ChainedTransformer chain = new ChainedTransformer(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"})}); chain.transform(123); } }
分析一下是怎么做到的
最外面的是一个 ChainedTransformer
,参数是一个 Transformer
数组,分别有四个 Transformer
1
上面说到这样可以做到命令执行
上面这个,在调用transform方法
的时候,需要传递一个Runtime.getRuntime()
,这几乎是不可能的,代码作者不会在反序列化后调用transform方法
还传递一个Runtime的实例
进去的,所以我们不可能这样直接RCE
2
但是我们可以结合ChainedTransformer
来进行RCE,如下
看看发生了什么
后面的chain.transform(123)
调用了transform方法,
obj是123,第一个循环也就是 ConstantTransformer(Runtime.getRuntime())
调用transform方法
,可得返回的是Runtime.getRuntime()对象
,而且Runtime.getRuntime()
对象作为下一次循环的obj参数;
obj是Runtime.getRuntime(),第二个循环是 InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
调用了transform方法
,就是说回到了反射的这个地方,最终命令执行
3
此时只要ChainedTransformer反序列化后调用transform方法并传递任意内容即可实现RCE,但是当尝试去序列化的时候,会发生了一个问题
因为这里的Runtime.getRuntime()返回的是一个Runtime的实例,而Runtime并没有继承Serializable,所以这里会序列化失败
就需要用到最终的POC了,这里详情可以看下图代码注释(问题:为什么Method 类型的Runtime.getRuntime()调用invoke()就会返回一个Runtime类型的对象呢)
相当于分析了这个CC1的sink了,那么下面来看看怎么做
上面的代码里面我们都是自己调用transform方法
的,现在我们需要那些调用了transform方法
而且是合适的,也就是LazyMap
再向上找,看哪里可以调用一个get方法
(这个也太多了,这实际情况中该怎么去找呢???)
找到的是 invoke:69, AnnotationInvocationHandler (sun.reflect.annotation)
下面就是代理的问题了,这里很妙,CC1的两个部分都很妙
代理部分
上面说到的AnnotationInvocationHandler类
,继承了InvocationHandler
, Serializable
,
这个对象既可以反序列化,又可以做动态代理对象
代理是什么
代理:动态代理对象每执行一个方法的时候,都会被转发到实现InvocationHandler接口类
的invoke方法
最终的POC里,分为两个代理部分
等等,这里为什么需要两个代理类,一个不就好了吗,直接代理类传入LazyMap
,然后因为是代理类,调用invoke
里面的get
,美滋滋?
当然不是,请看分析
1# 两个代理
翻看POC是怎么造成RCE的(从下到上分析) AnnotationInvocationHandler对象1 AnnotationInvocationHandler readobj (this.memberValues是proxy_map,proxy_map也是一个AnnotationInvocationHandler对象2,代理了lazymap) AnnotationInvocationHandler对象 this.memberValues.entrySet().iterator(); 即proxy_map.entrySet().iterator(); 即proxy_map.invoke() 即AnnotationInvocationHandler对象1.invoke_1() 相当于执行 AnnotationInvocationHandler对象2.invoke() (this.memberValues是lazymap) 然后去到get 即lazymap.get 然后就到下面
以上流程都可以经过IDEA debug验证,如下图
--
2# 一个代理
假如只设置一个AnnotationInvocationHandler对象,而且传入LazyMap 先readobj(this.memberValues是LazyMap) 那么就会经过288行 this.memberValues.entrySet().iterator(); 也就是LazyMap.entrySet().iterator()
那么答案就出来了,最终我们只需要做到序列化我们的构造的AnnotationInvocationHandler对象
,让漏洞应用序列化我们的恶意对象,即可以RCE。
小总结
这个链的学习,看到了很棒的思路,但是其中有一些细节在实际挖掘中不知道是怎么操作的,比如那个get那么多怎么知道呢,所以感觉还是需要多熟悉多很多链子才可以得心应手。
如何去挖掘这个利用链?
思路,方法,还有其他?
分析参数是否可控,分析sink
,分析是否继承是否可以反序列化, 从已知的source
和sink
入手,利用tabby之类的工具快速分析缩小范围。
不足
中间看住看不懂的点应该单独拿出来调试,调试几遍就好了。而且要像参考文章那样写多几个小Demo才可以顺利一点。
还有就是看清楚不懂的概念。
评论
发表评论