Coverage for /private/tmp/im/impacket/impacket/smbconnection.py : 63%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Author: Alberto Solino (@agsolino) # # Description: # # Wrapper class for SMB1/2/3 so it's transparent for the client. # You can still play with the low level methods (version dependent) # by calling getSMBServer() #
FILE_SHARE_WRITE, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE, FILE_OVERWRITE_IF, FILE_ATTRIBUTE_NORMAL, \ SMB2_IL_IMPERSONATION, SMB2_OPLOCK_LEVEL_NONE, FILE_READ_DATA , FILE_WRITE_DATA, FILE_OPEN, GENERIC_READ, GENERIC_WRITE, \ FILE_OPEN_REPARSE_POINT, MOUNT_POINT_REPARSE_DATA_STRUCTURE, FSCTL_SET_REPARSE_POINT, SMB2_0_IOCTL_IS_FSCTL, \ MOUNT_POINT_REPARSE_GUID_DATA_STRUCTURE, FSCTL_DELETE_REPARSE_POINT
# So the user doesn't need to import smb, the smb3 are already in here
""" SMBConnection class
:param string remoteName: name of the remote host, can be its NETBIOS name, IP or *\*SMBSERVER*. If the later, and port is 139, the library will try to get the target's server name. :param string remoteHost: target server's remote address (IPv4, IPv6) or FQDN :param string/optional myName: client's NETBIOS name :param integer/optional sess_port: target port to connect :param integer/optional timeout: timeout in seconds when receiving packets :param optional preferredDialect: the dialect desired to talk with the target server. If not specified the highest one available will be used :param optional boolean manualNegotiate: the user manually performs SMB_COM_NEGOTIATE
:return: a SMBConnection instance, if not raises a SessionError exception """
existingConnection=None, manualNegotiate=False):
# Existing Connection must be a smb or smb3 instance assert ( isinstance(existingConnection,smb.SMB) or isinstance(existingConnection, smb3.SMB3)) self._SMBConnection = existingConnection self._preferredDialect = self._SMBConnection.getDialect() self._doKerberos = self._SMBConnection.getKerberos() return
##preferredDialect = smb.SMB_DIALECT
flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation
:param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake
:return: True, raises a Session Error if error. """
# If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old # applications still believing # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i # *SMBSERVER's limitations # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best except: pass else:
# If no preferredDialect sent, we try the highest available one. self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) # Answer is SMB2 packet self._sess_port, self._timeout, session=self._nmbSession, negSessionResponse=SMB2Packet(packet)) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: self._sess_port, self._timeout) self._sess_port, self._timeout, preferredDialect=preferredDialect) else: raise Exception("Unknown dialect %s")
# propagate flags to the smb sub-object, except for Unicode (if server supports) # does not affect smb3 objects flags2 |= smb.SMB.FLAGS2_UNICODE
flags2=0, data=None): # Here we follow [MS-SMB2] negotiation handshake trying to understand what dialects # (including SMB1) is supported on the other end.
# FLAGS2_UNICODE is required by some stacks to continue, regardless of subsequent support timeout)
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
# No luck, quitting raise Exception('No answer!')
return self._nmbSession
""" returns the SMB/SMB3 instance being used. Useful for calling low level methods """
return self._SMBConnection.get_client_name()
return self._SMBConnection.set_remote_name(name)
return self._SMBConnection.get_server_os_major()
return self._SMBConnection.get_server_os_minor()
return self._SMBConnection.get_server_os_build()
return self._SMBConnection.is_signing_required()
return self._SMBConnection.getIOCapabilities()
""" logins into the target system
:param string user: username :param string password: password for the user :param string domain: domain where the account is valid for :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param bool ntlmFallback: If True it will try NTLMv1 authentication if NTLMv2 fails. Only available for SMBv1
:return: None, raises a Session Error if error. """ else: except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
TGS=None, useCache=True): """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.
:param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False
:return: None, raises a Session Error if error. """
useCache = False
# No cache present else: LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) # retrieve domain information from CCache file if needed if domain == '': domain = ccache.principal.realm['data'].decode('utf-8') LOG.debug('Domain retrieved from CCache: %s' % domain)
principal = 'cifs/%s@%s' % (self.getRemoteName().upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug("No valid credentials found in cache. ") else: TGS = creds.toTGS(principal) LOG.debug('Using TGS from cache')
# retrieve user information from CCache file if needed if user == '' and creds is not None: user = creds['client'].prettyPrint().split(b'@')[0] LOG.debug('Username retrieved from CCache: %s' % user) elif user == '' and len(ccache.principal.components) > 0: user = ccache.principal.components[0]['data'] LOG.debug('Username retrieved from CCache: %s' % user)
TGT, TGS) TGS) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet()) except KerberosError as e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # We might face this if the target does not support AES # So, if that's the case we'll force using RC4 by converting # the password to lm/nt hashes and hope for the best. If that's already # done, byebye. if lmhash is '' and nthash is '' and (aesKey is '' or aesKey is None) and TGT is None and TGS is None: lmhash = compute_lmhash(password) nthash = compute_nthash(password) else: raise e else: raise e
try: return self._SMBConnection.isGuestSession() except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
raise SessionError(e.get_error_code(), e.get_error_packet())
# If we already have a UNC we do nothing. # Else we build it except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" get a list of available shares at the connected target
:return: a list containing dict entries for each share, raises exception if error """ # Get the shares through RPC smb_connection=self)
""" list the files/directories under shareName/path
:param string shareName: a valid name for the share where the files/directories are going to be searched :param string path: a base path relative to shareName :param string password: the password for the share
:return: a list containing smb.SharedFile items, raises a SessionError exception if error. """
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
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 :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. """
LOG.error("CreateContexts not supported in SMB1")
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet()) else: creationDisposition, fileAttributes, impersonationLevel, securityFlags, oplockLevel, createContexts) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
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. """
LOG.error("CreateContexts not supported in SMB1")
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet()) else: creationDisposition, fileAttributes, impersonationLevel, securityFlags, oplockLevel, createContexts)
""" writes data to a file
:param HANDLE treeId: a valid handle for the share where the file is to be written :param HANDLE fileId: a valid handle for the file :param string data: buffer with the data to write :param integer offset: offset where to start writing the data
:return: amount of bytes written, if not raises a SessionError exception. """
""" reads data from a file
:param HANDLE treeId: a valid handle for the share where the file is to be read :param HANDLE fileId: a valid handle for the file to be read :param integer offset: offset where to start reading the data :param integer bytesToRead: amount of bytes to attempt reading. If None, it will attempt to read Dialect['MaxBufferSize'] bytes. :param boolean singleCall: If True it won't attempt to read all bytesToRead. It will only make a single read call
:return: the data read, if not raises a SessionError exception. Length of data read is not always bytesToRead """ else: toRead = b'' break else:
# End of the file achieved. finished = True else: offset += len(bytesRead) remainingBytesToRead -= len(bytesRead)
""" closes a file handle
:param HANDLE treeId: a valid handle for the share where the file is to be opened :param HANDLE fileId: a valid handle for the file/directory to be closed
:return: None, raises a SessionError exception if error.
""" except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" removes a file
:param string shareName: a valid name for the share where the file is to be deleted :param string pathName: the path name to remove
:return: None, raises a SessionError exception if error.
""" except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" queries basic information about an opened file/directory
:param HANDLE treeId: a valid handle for the share where the file is to be opened :param HANDLE fileId: a valid handle for the file/directory to be closed
:return: a smb.SMBQueryFileBasicInfo structure. raises a SessionError exception if error.
""" try: if self.getDialect() == smb.SMB_DIALECT: res = self._SMBConnection.query_file_info(treeId, fileId) else: res = self._SMBConnection.queryInfo(treeId, fileId) return smb.SMBQueryFileStandardInfo(res) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" creates a directory
:param string shareName: a valid name for the share where the directory is to be created :param string pathName: the path name or the directory to create
:return: None, raises a SessionError exception if error.
""" except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" deletes a directory
:param string shareName: a valid name for the share where directory is to be deleted :param string pathName: the path name or the directory to delete
:return: None, raises a SessionError exception if error.
"""
""" waits for a named pipe
:param HANDLE treeId: a valid handle for the share where the pipe is :param string pipeName: the pipe name to check :param integer timeout: time to wait for an answer
:return: None, raises a SessionError exception if error.
""" try: return self._SMBConnection.waitNamedPipe(treeId, pipeName, timeout = timeout) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" writes to a named pipe using a transaction command
:param HANDLE treeId: a valid handle for the share where the pipe is :param HANDLE fileId: a valid handle for the pipe :param string data: buffer with the data to write :param boolean waitAnswer: whether or not to wait for an answer
:return: None, raises a SessionError exception if error.
""" try: return self._SMBConnection.TransactNamedPipe(treeId, fileId, data, waitAnswer = waitAnswer) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" reads from a named pipe using a transaction command
:return: data read, raises a SessionError exception if error.
""" try: return self._SMBConnection.TransactNamedPipeRecv() except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" writes to a named pipe
:param HANDLE treeId: a valid handle for the share where the pipe is :param HANDLE fileId: a valid handle for the pipe :param string data: buffer with the data to write :param boolean waitAnswer: whether or not to wait for an answer
:return: None, raises a SessionError exception if error.
""" try: if self.getDialect() == smb.SMB_DIALECT: return self._SMBConnection.write_andx(treeId, fileId, data, wait_answer = waitAnswer, write_pipe_mode = True) else: return self.writeFile(treeId, fileId, data, 0) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" read from a named pipe
:param HANDLE treeId: a valid handle for the share where the pipe resides :param HANDLE fileId: a valid handle for the pipe :param integer bytesToRead: amount of data to read
:return: None, raises a SessionError exception if error.
"""
try: return self.readFile(treeId, fileId, bytesToRead = bytesToRead, singleCall = True) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" downloads a file
:param string shareName: name for the share where the file is to be retrieved :param string pathName: the path name to retrieve :param callback callback: function called to write the contents read. :param int shareAccessMode:
:return: None, raises a SessionError exception if error.
""" # if share access mode is none, let's the underlying API deals with it else: return self._SMBConnection.retr_file(shareName, pathName, callback, shareAccessMode=shareAccessMode) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" uploads a file
:param string shareName: name for the share where the file is to be uploaded :param string pathName: the path name to upload :param callback callback: function called to read the contents to be written. :param int shareAccessMode:
:return: None, raises a SessionError exception if error.
""" # if share access mode is none, let's the underlying API deals with it else: return self._SMBConnection.stor_file(shareName, pathName, callback, shareAccessMode) except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" creates a mount point at an existing directory
:param int tid: tree id of current connection :param string path: directory at which to create mount point (must already exist) :param string target: target address of mount point """
# Verify we're under SMB2+ session if self.getDialect() not in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: raise SessionError(error = nt_errors.STATUS_NOT_SUPPORTED)
fid = self.openFile(tid, path, GENERIC_READ | GENERIC_WRITE, creationOption=FILE_OPEN_REPARSE_POINT)
if target.startswith("\\"): fixed_name = target.encode('utf-16le') else: fixed_name = ("\\??\\" + target).encode('utf-16le')
name = target.encode('utf-16le')
reparseData = MOUNT_POINT_REPARSE_DATA_STRUCTURE()
reparseData['PathBuffer'] = fixed_name + b"\x00\x00" + name + b"\x00\x00" reparseData['SubstituteNameLength'] = len(fixed_name) reparseData['PrintNameOffset'] = len(fixed_name) + 2 reparseData['PrintNameLength'] = len(name)
self._SMBConnection.ioctl(tid, fid, FSCTL_SET_REPARSE_POINT, flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=reparseData)
self.closeFile(tid, fid)
""" removes a mount point without deleting the underlying directory
:param int tid: tree id of current connection :param string path: path to mount point to remove """
# Verify we're under SMB2+ session if self.getDialect() not in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: raise SessionError(error = nt_errors.STATUS_NOT_SUPPORTED)
fid = self.openFile(tid, path, GENERIC_READ | GENERIC_WRITE, creationOption=FILE_OPEN_REPARSE_POINT)
reparseData = MOUNT_POINT_REPARSE_GUID_DATA_STRUCTURE()
reparseData['DataBuffer'] = b""
try: self._SMBConnection.ioctl(tid, fid, FSCTL_DELETE_REPARSE_POINT, flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=reparseData) except (smb.SessionError, smb3.SessionError) as e: self.closeFile(tid, fid) raise SessionError(e.get_error_code(), e.get_error_packet())
self.closeFile(tid, fid)
""" renames a file/directory
:param string shareName: name for the share where the files/directories are :param string oldPath: the old path name or the directory/file to rename :param string newPath: the new path name or the directory/file to rename
:return: True, raises a SessionError exception if error.
"""
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
""" reconnects the SMB object based on the original options and credentials used. Only exception is that manualNegotiate will not be honored. Not only the connection will be created but also a login attempt using the original credentials and method (Kerberos, PtH, etc)
:return: True, raises a SessionError exception if error """ self.kerberosLogin(userName, password, domain, lmhash, nthash, aesKey, self._kdcHost, TGT, TGS, self._useCache) else:
except (smb.SessionError, smb3.SessionError) as e: raise SessionError(e.get_error_code(), e.get_error_packet())
else:
if self.getDialect() == smb.SMB_DIALECT: return self._SMBConnection.set_session_key(key) else: return self._SMBConnection.setSessionKey(key)
""" logs off and closes the underlying _NetBIOSSession()
:return: None """
""" This is the exception every client should catch regardless of the underlying SMB version used. We'll take care of that. NETBIOS exceptions are NOT included, since all SMB versions share the same NETBIOS instances. """
return self.error
return self.packet
return nt_errors.ERROR_MESSAGES[self.error]
else: return 'SMB SessionError: 0x%x' % self.error |