前文提到序列化漏洞问题,一般不太可能有readObject上有直接可以命令执行的问题,而是会存在调用一些比较特殊的方法,导致产生漏洞。
参考【1】, 很多文章介绍Java反序列化都是用的commons-connection1利用链,但是这条利用链对于新手来说不太友好,重点不在反序列化,而在于commons-connection1本身LazyMap这条链上。这里以ysoserial Groovy1为例分析下原理。
Show Me The Code
1 | /** |
整个利用代码可以分为两段
构造1个特殊的Object,这个Object有某个特殊方法可以触发命令执行
找到一个重写readObject的Serializable类,且这个类readObject过程中有可能触发1中的那个特殊方法
1. 能够命令执行的特殊方法
能够执行命令的,一般都是因为invoke方法,自然定位到ConvertedClosure的invoke方法
ConversionHandler.class
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
ConvertedClosure.class
1 | public Object invokeCustom(Object proxy, Method method, Object[] args) throws Throwable { |
搞懂了上面两个方法方法方法就不难看明白下面构造的的poc
1 | MethodClosure methodClosure = new MethodClosure(command,"execute"); |
主要此处的Proxy.newProxyInstance,为创建动态代理,含义为Object o,以后所有有关allInterfaces(其实也就是Map.class)的接口,都交给closure处理。
1 | //newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) |
2. 那个有特殊readObject的Serializable类
这里直接给出sun.reflect.annotation.AnnotationInvocationHandler
多看几个ysoserial的例子,跟一遍AnnotationInvocationHandler 的readObject方法,就会懂这个套路(套路是什么?通用的方法,以后也可以拿来用)。
AnnotationInvocationHandler 有一个属性memberValues,为Map类型,在readObject 中会对该memberValues调用entrySet。如此触发1中map.entrySet().iterator()。
1 | Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); |
这段代码也解释下,作用为创建一个AnnotationInvocationHandler类,构造参数为map。
之所以不能new是因为非public,只能通过反射来创建。
AnnotationInvocationHandler 是java的一个注释类,具体用来干什么的可以再拓展学习下,参考【2】
利用链
1 | Gadget chain: |
小结
本节简单跟进了ysoserial Groovy1的反序列化利用过程,其实跟进并不难,难的是去逆向思维,为什么会用到methodClosure、ConvertedClosure,参数为什么是execute、entrySet?
为什么是AnnotationInvocationHandler会被作为通用的触发器,还有没有其他的?
为什么会定位到methodClosure、ConvertedClosure
通过invoke、InvokeHandler应该可以定位到,但是具体参数如何构造,那考验的就是Java功底了。AnnotationInvocationHandler
还有其他的类似的类吗?
如何举一反三,发现新的Gadget?现在功力不够,留待以后吧