Java代码审计中的SpEL注入
SpEL表达式的功能与使用
功能
- 访问对象属性:SpEL表达式可以通过对象引用来访问对象的属性,例如${object.property}。
- 调用方法:SpEL表达式可以调用对象的方法,例如${object.method()}。
- 进行算术运算:SpEL表达式支持各种算术运算符,如加法、减法、乘法和除法。
- 进行逻辑运算:SpEL表达式支持逻辑运算符,如与、或、非等。
- 进行条件判断:SpEL表达式可以进行条件判断,例如通过if语句判断条件,并执行相应的操作。
- 访问集合元素和属性:SpEL表达式可以通过索引或键来访问集合中的元素或对象的属性。
- 执行正则表达式匹配:SpEL表达式可以执行正则表达式匹配,并返回匹配结果。
- 访问上下文变量和参数:SpEL表达式可以访问上下文中的变量和方法参数。
- 进行类型转换:SpEL表达式可以进行类型转换操作,将一个对象转换为另一种类型。
- 支持特殊操作符:SpEL表达式支持一些特殊的操作符,如Elvis操作符(?:)、安全导航操作符(?.)等。
使用
表达式
一般SpEL表达式语法与python语法有些像,此处有详细总结:Spring-SpEL表达式超级详细使用全解-CSDN博客
这里来看几个较为特殊的用法
spel语法中的T()操作符 , T()操作符会返回一个object , 它可以帮助我们获取某个类的静态方法 , 用法T(全限定类名).方法名(),后面会用得到
spel中的#操作符可以用于标记对象,SpEL 表达式可以用 #变量名 的形式访问它们
获取类的类型
可以使用特殊的T运算符来指定java.lang.Class的实例(类型)。静态方法也是通过使用这个操作符来调用的。1
2
3
4
5
6
7
8
9
10ExpressionParser parser = new SpelExpressionParser();
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue = parser.parseExpression(
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class);用
#变量名的形式访问变量1
2context.setVariable("x", 10);
Integer result = parser.parseExpression("#x + 20").getValue(context, Integer.class); // 结果:30表达式模板
1
2
3
4
5
6
7// 通常使用#{}作为模板,与字符串拼接起来
String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
new TemplateParserContext()).getValue(String.class);
// evaluates to "random number is 0.7038186818312008"或者找到源码中定义的地方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// TemplateParserContext 的定义
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
调用
SpEL调用流程 : 1.新建解析器 2.解析表达式 3.注册变量(可省,在取值之前注册) 4.取值
示例1:不注册新变量的用法
1 | |
示例2:自定义注册加载变量的用法
1 | |
SpEL表达式注入攻击
攻击流程
攻击条件(敏感函数)
- 使用StandardEvaluationContext,
- 未对输入的SpEL进行校验,或有方法绕过
- 对表达式调用了getValue()或parseExpression()函数或getAdvanceValue函数。
关键字
- getValue(),parseExpression(),getAdvanceValue()
- StandardEvaluationContext(),ExpressionParser(),SpelExpressionParser()
Code-Breaking javacon
下载源码
https://www.leavesongs.com/media/attachment/2018/11/23/challenge-0.0.1-SNAPSHOT.jar
使用命令运行环境
1 | |
使用JD-GUI反编译(直接用IDEA反编译是不成功的),导出后用IDEA打开
查看目录结构以及application.yml文件

可以看到是有spel表达式调用的,但是也有黑名单
查看MainController文件
可以看到getAdvanceValue函数(动态解析用户输入的 Spring 表达式(SpEL)并返回执行结果,它在解析前会对输入内容做黑名单关键词过滤,阻止执行危险表达式),此函数是SpELl注入漏洞出发点

搜索关键字getAdvanceValue
可以看到admin方法中若rememberMeValue不为空,则会将此值做解密处理,将获得的值作为username属性
此时的username可控,可以实现SpEL注入。
1 | |
因此,我们只需输入admin/admin并勾选remember-me选项,点击登录,然后在请求包中修改Cookie内容即可。
先构造payload
由于黑名单的限制,这里利用java反射机制调用所需类
1 | |
分析
1 | |
再将其加密,加密代码在Encryptor.java文件中

已知key是c0dehack1nghere1,initVector是0123456789abcdef,value是要加密的值
写一个加密脚本
先将value设置为admin,加密后与数据包中remember-me的值比较
1 | |
可以看到加密结果与数据包中值是相同的,说明脚本没有问题


将脚本中value值更改为payload
运行后抓取数据包,将remember-me的值改为加密后结果
发送数据包,成功弹出计算器

通过SpEL注入内存马
https://gv7.me/articles/2022/the-spring-cloud-gateway-inject-memshell-through-spel-expressions/
