aboutsummaryrefslogtreecommitdiff
path: root/tests/python_dependencies/impacket/smb3.py
diff options
context:
space:
mode:
authorMax Dymond <max.dymond@metaswitch.com>2017-06-30 13:53:19 +0100
committerDaniel Stenberg <daniel@haxx.se>2017-07-04 10:36:06 +0200
commitf1609155d54c82b535f50a6b4693b2be47d272aa (patch)
tree57545c4cf2c182c2a2a57df587006f8f22db4f83 /tests/python_dependencies/impacket/smb3.py
parentfc2e81c38b19ebe9651cda28a2395a0244125f52 (diff)
test: add impacket for SMB testing
Import impacket 0.9.15 for use in SMB testing. This was generated by doing "pip2.7 install -t . impacket" Unnecessary files for current testing were deleted.
Diffstat (limited to 'tests/python_dependencies/impacket/smb3.py')
-rw-r--r--tests/python_dependencies/impacket/smb3.py1629
1 files changed, 1629 insertions, 0 deletions
diff --git a/tests/python_dependencies/impacket/smb3.py b/tests/python_dependencies/impacket/smb3.py
new file mode 100644
index 000000000..5548e4b0c
--- /dev/null
+++ b/tests/python_dependencies/impacket/smb3.py
@@ -0,0 +1,1629 @@
+# Copyright (c) 2003-2016 CORE Security Technologies
+#
+# 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:
+# [MS-SMB2] Protocol Implementation (SMB2 and SMB3)
+# As you might see in the code, it's implemented strictly following
+# the structures defined in the protocol specification. This may
+# not be the most efficient way (e.g. self._Connection is the
+# same to self._Session in the context of this library ) but
+# it certainly helps following the document way easier.
+#
+# ToDo:
+# [X] Implement SMB2_CHANGE_NOTIFY
+# [X] Implement SMB2_QUERY_INFO
+# [X] Implement SMB2_SET_INFO
+# [ ] Implement SMB2_OPLOCK_BREAK
+# [X] Implement SMB3 signing
+# [ ] Implement SMB3 encryption
+# [ ] Add more backward compatible commands from the smb.py code
+# [ ] Fix up all the 'ToDo' comments inside the code
+#
+
+import socket
+import ntpath
+import random
+import string
+import struct
+from binascii import a2b_hex
+from contextlib import contextmanager
+
+from impacket import nmb, ntlm, uuid, crypto, LOG
+from impacket.smb3structs import *
+from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \
+ STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES
+from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp
+
+
+# For signing
+import hashlib, hmac, copy
+
+# Structs to be used
+TREE_CONNECT = {
+ 'ShareName' : '',
+ 'TreeConnectId' : 0,
+ 'Session' : 0,
+ 'IsDfsShare' : False,
+ # If the client implements the SMB 3.0 dialect,
+ # the client MUST also implement the following
+ 'IsCAShare' : False,
+ 'EncryptData' : False,
+ 'IsScaleoutShare' : False,
+ # Outside the protocol
+ 'NumberOfUses' : 0,
+}
+
+FILE = {
+ 'OpenTable' : [],
+ 'LeaseKey' : '',
+ 'LeaseState' : 0,
+ 'LeaseEpoch' : 0,
+}
+
+OPEN = {
+ 'FileID' : '',
+ 'TreeConnect' : 0,
+ 'Connection' : 0, # Not Used
+ 'Oplocklevel' : 0,
+ 'Durable' : False,
+ 'FileName' : '',
+ 'ResilientHandle' : False,
+ 'LastDisconnectTime' : 0,
+ 'ResilientTimeout' : 0,
+ 'OperationBuckets' : [],
+ # If the client implements the SMB 3.0 dialect,
+ # the client MUST implement the following
+ 'CreateGuid' : '',
+ 'IsPersistent' : False,
+ 'DesiredAccess' : '',
+ 'ShareMode' : 0,
+ 'CreateOption' : '',
+ 'FileAttributes' : '',
+ 'CreateDisposition' : '',
+}
+
+REQUEST = {
+ 'CancelID' : '',
+ 'Message' : '',
+ 'Timestamp' : 0,
+}
+
+CHANNEL = {
+ 'SigningKey' : '',
+ 'Connection' : 0,
+}
+
+
+class SessionError(Exception):
+ def __init__( self, error = 0, packet=0):
+ Exception.__init__(self)
+ self.error = error
+ self.packet = packet
+
+ def get_error_code( self ):
+ return self.error
+
+ def get_error_packet( self ):
+ return self.packet
+
+ def __str__( self ):
+ return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error])
+
+
+class SMB3:
+ def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=60, UDP = 0, preferredDialect = None, session = None):
+
+ # [MS-SMB2] Section 3
+ self.RequireMessageSigning = False #
+ self.ConnectionTable = {}
+ self.GlobalFileTable = {}
+ self.ClientGuid = ''.join([random.choice(string.letters) for i in range(16)])
+ # Only for SMB 3.0
+ self.EncryptionAlgorithmList = ['AES-CCM']
+ self.MaxDialect = []
+ self.RequireSecureNegotiate = False
+
+ # Per Transport Connection Data
+ self._Connection = {
+ # Indexed by SessionID
+ #'SessionTable' : {},
+ # Indexed by MessageID
+ 'OutstandingRequests' : {},
+ 'OutstandingResponses' : {}, #
+ 'SequenceWindow' : 0, #
+ 'GSSNegotiateToken' : '', #
+ 'MaxTransactSize' : 0, #
+ 'MaxReadSize' : 0, #
+ 'MaxWriteSize' : 0, #
+ 'ServerGuid' : '', #
+ 'RequireSigning' : False, #
+ 'ServerName' : '', #
+ # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST
+ # also implement the following
+ 'Dialect' : '', #
+ 'SupportsFileLeasing' : False, #
+ 'SupportsMultiCredit' : False, #
+ # If the client implements the SMB 3.0 dialect,
+ # it MUST also implement the following
+ 'SupportsDirectoryLeasing' : False, #
+ 'SupportsMultiChannel' : False, #
+ 'SupportsPersistentHandles': False, #
+ 'SupportsEncryption' : False, #
+ 'ClientCapabilities' : 0,
+ 'ServerCapabilities' : 0, #
+ 'ClientSecurityMode' : 0, #
+ 'ServerSecurityMode' : 0, #
+ # Outside the protocol
+ 'ServerIP' : '', #
+ }
+
+ self._Session = {
+ 'SessionID' : 0, #
+ 'TreeConnectTable' : {}, #
+ 'SessionKey' : '', #
+ 'SigningRequired' : False, #
+ 'Connection' : 0, #
+ 'UserCredentials' : '', #
+ 'OpenTable' : {}, #
+ # If the client implements the SMB 3.0 dialect,
+ # it MUST also implement the following
+ 'ChannelList' : [],
+ 'ChannelSequence' : 0,
+ #'EncryptData' : False,
+ 'EncryptData' : True,
+ 'EncryptionKey' : '',
+ 'DecryptionKey' : '',
+ 'SigningKey' : '',
+ 'ApplicationKey' : '',
+ # Outside the protocol
+ 'SessionFlags' : 0, #
+ 'ServerName' : '', #
+ 'ServerDomain' : '', #
+ 'ServerDNSDomainName' : '', #
+ 'ServerOS' : '', #
+ 'SigningActivated' : False, #
+ }
+
+ self.SMB_PACKET = SMB2Packet
+
+ self._timeout = timeout
+ self._Connection['ServerIP'] = remote_host
+ self._NetBIOSSession = None
+
+ self.__userName = ''
+ self.__password = ''
+ self.__domain = ''
+ self.__lmhash = ''
+ self.__nthash = ''
+ self.__kdc = ''
+ self.__aesKey = ''
+ self.__TGT = None
+ self.__TGS = None
+
+ if sess_port == 445 and remote_name == '*SMBSERVER':
+ self._Connection['ServerName'] = remote_host
+ else:
+ self._Connection['ServerName'] = remote_name
+
+ if session is None:
+ if not my_name:
+ my_name = socket.gethostname()
+ i = string.find(my_name, '.')
+ if i > -1:
+ my_name = my_name[:i]
+
+ if UDP:
+ self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
+ else:
+ self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
+
+ self.negotiateSession(preferredDialect)
+ else:
+ self._NetBIOSSession = session
+ # We should increase the SequenceWindow since a packet was already received.
+ self._Connection['SequenceWindow'] += 1
+ # Let's negotiate again using the same connection
+ self.negotiateSession(preferredDialect)
+
+ def printStatus(self):
+ print "CONNECTION"
+ for i in self._Connection.items():
+ print "%-40s : %s" % i
+ print
+ print "SESSION"
+ for i in self._Session.items():
+ print "%-40s : %s" % i
+
+ def getServerName(self):
+ return self._Session['ServerName']
+
+ def getServerIP(self):
+ return self._Connection['ServerIP']
+
+ def getServerDomain(self):
+ return self._Session['ServerDomain']
+
+ def getServerDNSDomainName(self):
+ return self._Session['ServerDNSDomainName']
+
+ def getServerOS(self):
+ return self._Session['ServerOS']
+
+ def getServerOSMajor(self):
+ return self._Session['ServerOSMajor']
+
+ def getServerOSMinor(self):
+ return self._Session['ServerOSMinor']
+
+ def getServerOSBuild(self):
+ return self._Session['ServerOSBuild']
+
+ def isGuestSession(self):
+ return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST
+
+ def setTimeout(self, timeout):
+ self._timeout = timeout
+
+ @contextmanager
+ def useTimeout(self, timeout):
+ prev_timeout = self.getTimeout(timeout)
+ try:
+ yield
+ finally:
+ self.setTimeout(prev_timeout)
+
+ def getDialect(self):
+ return self._Connection['Dialect']
+
+
+ def signSMB(self, packet):
+ packet['Signature'] = '\x00'*16
+ if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002:
+ if len(self._Session['SessionKey']) > 0:
+ signature = hmac.new(self._Session['SessionKey'], str(packet), hashlib.sha256).digest()
+ packet['Signature'] = signature[:16]
+ else:
+ if len(self._Session['SessionKey']) > 0:
+ p = str(packet)
+ signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p))
+ packet['Signature'] = signature
+
+ def sendSMB(self, packet):
+ # The idea here is to receive multiple/single commands and create a compound request, and send it
+ # Should return the MessageID for later retrieval. Implement compounded related requests.
+
+ # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or
+ # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the
+ # SMB2 header to Session.ChannelSequence
+
+ # Check this is not a CANCEL request. If so, don't consume sequece numbers
+ if packet['Command'] is not SMB2_CANCEL:
+ packet['MessageID'] = self._Connection['SequenceWindow']
+ self._Connection['SequenceWindow'] += 1
+ packet['SessionID'] = self._Session['SessionID']
+
+ # Default the credit charge to 1 unless set by the caller
+ if packet.fields.has_key('CreditCharge') is False:
+ packet['CreditCharge'] = 1
+
+ # Standard credit request after negotiating protocol
+ if self._Connection['SequenceWindow'] > 3:
+ packet['CreditRequestResponse'] = 127
+
+ messageId = packet['MessageID']
+
+ if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
+ if packet['TreeID'] > 0 and self._Session['TreeConnectTable'].has_key(packet['TreeID']) is True:
+ if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False:
+ packet['Flags'] = SMB2_FLAGS_SIGNED
+ self.signSMB(packet)
+ elif packet['TreeID'] == 0:
+ packet['Flags'] = SMB2_FLAGS_SIGNED
+ self.signSMB(packet)
+
+ if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True):
+ plainText = str(packet)
+ transformHeader = SMB2_TRANSFORM_HEADER()
+ transformHeader['Nonce'] = ''.join([random.choice(string.letters) for i in range(11)])
+ transformHeader['OriginalMessageSize'] = len(plainText)
+ transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM
+ transformHeader['SessionID'] = self._Session['SessionID']
+ from Crypto.Cipher import AES
+ try:
+ AES.MODE_CCM
+ except:
+ LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
+ raise
+ cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, transformHeader['Nonce'])
+ cipher.update(str(transformHeader)[20:])
+ cipherText = cipher.encrypt(plainText)
+ transformHeader['Signature'] = cipher.digest()
+ packet = str(transformHeader) + cipherText
+
+ self._NetBIOSSession.send_packet(str(packet))
+ return messageId
+
+ def recvSMB(self, packetID = None):
+ # First, verify we don't have the packet already
+ if self._Connection['OutstandingResponses'].has_key(packetID):
+ return self._Connection['OutstandingResponses'].pop(packetID)
+
+ data = self._NetBIOSSession.recv_packet(self._timeout)
+
+ if data.get_trailer().startswith('\xfdSMB'):
+ # Packet is encrypted
+ transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
+ from Crypto.Cipher import AES
+ try:
+ AES.MODE_CCM
+ except:
+ LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
+ raise
+ cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
+ cipher.update(str(transformHeader)[20:])
+ plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
+ #cipher.verify(transformHeader['Signature'])
+ packet = SMB2Packet(plainText)
+ else:
+ # In all SMB dialects for a response this field is interpreted as the Status field.
+ # This field can be set to any value. For a list of valid status codes,
+ # see [MS-ERREF] section 2.3.
+ packet = SMB2Packet(data.get_trailer())
+
+ # Loop while we receive pending requests
+ if packet['Status'] == STATUS_PENDING:
+ status = STATUS_PENDING
+ while status == STATUS_PENDING:
+ data = self._NetBIOSSession.recv_packet(self._timeout)
+ if data.get_trailer().startswith('\xfeSMB'):
+ packet = SMB2Packet(data.get_trailer())
+ else:
+ # Packet is encrypted
+ transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
+ from Crypto.Cipher import AES
+ try:
+ AES.MODE_CCM
+ except:
+ LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
+ raise
+ cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
+ cipher.update(str(transformHeader)[20:])
+ plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
+ #cipher.verify(transformHeader['Signature'])
+ packet = SMB2Packet(plainText)
+ status = packet['Status']
+
+ if packet['MessageID'] == packetID or packetID is None:
+ # if self._Session['SigningRequired'] is True:
+ # self.signSMB(packet)
+ # Let's update the sequenceWindow based on the CreditsCharged
+ self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1)
+ return packet
+ else:
+ self._Connection['OutstandingResponses'][packet['MessageID']] = packet
+ return self.recvSMB(packetID)
+
+ def negotiateSession(self, preferredDialect = None):
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_NEGOTIATE
+ negSession = SMB2Negotiate()
+
+ negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
+ if self.RequireMessageSigning is True:
+ negSession['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED
+ negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
+ negSession['ClientGuid'] = self.ClientGuid
+ if preferredDialect is not None:
+ negSession['Dialects'] = [preferredDialect]
+ else:
+ negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]
+ negSession['DialectCount'] = len(negSession['Dialects'])
+ packet['Data'] = negSession
+
+ # Storing this data for later use
+ self._Connection['ClientSecurityMode'] = negSession['SecurityMode']
+ self._Connection['Capabilities'] = negSession['Capabilities']
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ # ToDo this:
+ # If the DialectRevision in the SMB2 NEGOTIATE Response is 0x02FF, the client MUST issue a new
+ # SMB2 NEGOTIATE request as described in section 3.2.4.2.2.2 with the only exception
+ # that the client MUST allocate sequence number 1 from Connection.SequenceWindow, and MUST set
+ # MessageId field of the SMB2 header to 1. Otherwise, the client MUST proceed as follows.
+ negResp = SMB2Negotiate_Response(ans['Data'])
+ self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize'])
+ self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize'])
+ self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize'])
+ self._Connection['ServerGuid'] = negResp['ServerGuid']
+ self._Connection['GSSNegotiateToken'] = negResp['Buffer']
+ self._Connection['Dialect'] = negResp['DialectRevision']
+ if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED:
+ self._Connection['RequireSigning'] = True
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING:
+ self._Connection['SupportsFileLeasing'] = True
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU:
+ self._Connection['SupportsMultiCredit'] = True
+
+ if self._Connection['Dialect'] == SMB2_DIALECT_30:
+ # Switching to the right packet format
+ self.SMB_PACKET = SMB3Packet
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING:
+ self._Connection['SupportsDirectoryLeasing'] = True
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL:
+ self._Connection['SupportsMultiChannel'] = True
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES:
+ self._Connection['SupportsPersistentHandles'] = True
+ if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION:
+ self._Connection['SupportsEncryption'] = True
+
+ self._Connection['ServerCapabilities'] = negResp['Capabilities']
+ self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
+
+ def getCredentials(self):
+ return (
+ self.__userName,
+ self.__password,
+ self.__domain,
+ self.__lmhash,
+ self.__nthash,
+ self.__aesKey,
+ self.__TGT,
+ self.__TGS)
+
+ def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None):
+ # If TGT or TGS are specified, they are in the form of:
+ # TGS['KDC_REP'] = the response from the server
+ # TGS['cipher'] = the cipher used
+ # TGS['sessionKey'] = the sessionKey
+ # If we have hashes, normalize them
+ if lmhash != '' or nthash != '':
+ if len(lmhash) % 2: lmhash = '0%s' % lmhash
+ if len(nthash) % 2: nthash = '0%s' % nthash
+ try: # just in case they were converted already
+ lmhash = a2b_hex(lmhash)
+ nthash = a2b_hex(nthash)
+ except:
+ pass
+
+ self.__userName = user
+ self.__password = password
+ self.__domain = domain
+ self.__lmhash = lmhash
+ self.__nthash = nthash
+ self.__kdc = kdcHost
+ self.__aesKey = aesKey
+ self.__TGT = TGT
+ self.__TGS = TGS
+
+ sessionSetup = SMB2SessionSetup()
+ if self.RequireMessageSigning is True:
+ sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
+ else:
+ sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
+
+ sessionSetup['Flags'] = 0
+ #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
+
+ # Importing down here so pyasn1 is not required if kerberos is not used.
+ from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
+ from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
+ from impacket.krb5 import constants
+ from impacket.krb5.types import Principal, KerberosTime, Ticket
+ from pyasn1.codec.der import decoder, encoder
+ import datetime
+
+ # First of all, we need to get a TGT for the user
+ userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
+ if TGT is None:
+ if TGS is None:
+ tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
+ else:
+ tgt = TGT['KDC_REP']
+ cipher = TGT['cipher']
+ sessionKey = TGT['sessionKey']
+
+ # Save the ticket
+ # If you want, for debugging purposes
+# from impacket.krb5.ccache import CCache
+# ccache = CCache()
+# try:
+# if TGS is None:
+# ccache.fromTGT(tgt, oldSessionKey, sessionKey)
+# else:
+# ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] )
+# ccache.saveFile('/tmp/ticket.bin')
+# except Exception, e:
+# print e
+# pass
+
+ # Now that we have the TGT, we should ask for a TGS for cifs
+
+ if TGS is None:
+ serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value)
+ tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
+ else:
+ tgs = TGS['KDC_REP']
+ cipher = TGS['cipher']
+ sessionKey = TGS['sessionKey']
+
+ # Let's build a NegTokenInit with a Kerberos REQ_AP
+
+ blob = SPNEGO_NegTokenInit()
+
+ # Kerberos
+ blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
+
+ # Let's extract the ticket from the TGS
+ tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
+ ticket = Ticket()
+ ticket.from_asn1(tgs['ticket'])
+
+ # Now let's build the AP_REQ
+ apReq = AP_REQ()
+ apReq['pvno'] = 5
+ apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
+
+ opts = list()
+ apReq['ap-options'] = constants.encodeFlags(opts)
+ seq_set(apReq,'ticket', ticket.to_asn1)
+
+ authenticator = Authenticator()
+ authenticator['authenticator-vno'] = 5
+ authenticator['crealm'] = domain
+ seq_set(authenticator, 'cname', userName.components_to_asn1)
+ now = datetime.datetime.utcnow()
+
+ authenticator['cusec'] = now.microsecond
+ authenticator['ctime'] = KerberosTime.to_asn1(now)
+
+ encodedAuthenticator = encoder.encode(authenticator)
+
+ # Key Usage 11
+ # AP-REQ Authenticator (includes application authenticator
+ # subkey), encrypted with the application session key
+ # (Section 5.5.1)
+ encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
+
+ apReq['authenticator'] = None
+ apReq['authenticator']['etype'] = cipher.enctype
+ apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
+
+ blob['MechToken'] = encoder.encode(apReq)
+
+ sessionSetup['SecurityBufferLength'] = len(blob)
+ sessionSetup['Buffer'] = blob.getData()
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_SESSION_SETUP
+ packet['Data'] = sessionSetup
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ self._Session['SessionID'] = ans['SessionID']
+ self._Session['SigningRequired'] = self._Connection['RequireSigning']
+ self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
+ self._Session['Connection'] = self._NetBIOSSession.get_socket()
+
+ self._Session['SessionKey'] = sessionKey.contents[:16]
+ if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
+ self._Session['SigningKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCMAC\x00", "SmbSign\x00", 128)
+
+ # Calculate the key derivations for dialect 3.0
+ if self._Session['SigningRequired'] is True:
+ self._Session['SigningActivated'] = True
+ if self._Connection['Dialect'] == SMB2_DIALECT_30:
+ self._Session['ApplicationKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2APP\x00", "SmbRpc\x00", 128)
+ self._Session['EncryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerIn \x00", 128)
+ self._Session['DecryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerOut\x00", 128)
+
+ return True
+ else:
+ # We clean the stuff we used in case we want to authenticate again
+ # within the same connection
+ self._Session['UserCredentials'] = ''
+ self._Session['Connection'] = 0
+ self._Session['SessionID'] = 0
+ self._Session['SigningRequired'] = False
+ self._Session['SigningKey'] = ''
+ self._Session['SessionKey'] = ''
+ self._Session['SigningActivated'] = False
+ raise
+
+
+ def login(self, user, password, domain = '', lmhash = '', nthash = ''):
+ # If we have hashes, normalize them
+ if lmhash != '' or nthash != '':
+ if len(lmhash) % 2: lmhash = '0%s' % lmhash
+ if len(nthash) % 2: nthash = '0%s' % nthash
+ try: # just in case they were converted already
+ lmhash = a2b_hex(lmhash)
+ nthash = a2b_hex(nthash)
+ except:
+ pass
+
+ self.__userName = user
+ self.__password = password
+ self.__domain = domain
+ self.__lmhash = lmhash
+ self.__nthash = nthash
+ self.__aesKey = ''
+ self.__TGT = None
+ self.__TGS = None
+
+ sessionSetup = SMB2SessionSetup()
+ if self.RequireMessageSigning is True:
+ sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
+ else:
+ sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
+
+ sessionSetup['Flags'] = 0
+ #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
+
+ # Let's build a NegTokenInit with the NTLMSSP
+ # TODO: In the future we should be able to choose different providers
+
+ blob = SPNEGO_NegTokenInit()
+
+ # NTLMSSP
+ blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
+ auth = ntlm.getNTLMSSPType1('','', self._Connection['RequireSigning'])
+ blob['MechToken'] = str(auth)
+
+ sessionSetup['SecurityBufferLength'] = len(blob)
+ sessionSetup['Buffer'] = blob.getData()
+
+ # ToDo:
+ # If this authentication is for establishing an alternative channel for an existing Session, as specified
+ # in section 3.2.4.1.7, the client MUST also set the following values:
+ # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new
+ # channel being established.
+ # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field.
+ # The PreviousSessionId field MUST be set to zero.
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_SESSION_SETUP
+ packet['Data'] = sessionSetup
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
+ self._Session['SessionID'] = ans['SessionID']
+ self._Session['SigningRequired'] = self._Connection['RequireSigning']
+ self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
+ self._Session['Connection'] = self._NetBIOSSession.get_socket()
+ sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
+ respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
+
+ # Let's parse some data and keep it to ourselves in case it is asked
+ ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
+ if ntlmChallenge['TargetInfoFields_len'] > 0:
+ av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
+ if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
+ try:
+ self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
+ except:
+ # For some reason, we couldn't decode Unicode here.. silently discard the operation
+ pass
+ if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
+ try:
+ if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'):
+ self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
+ except:
+ # For some reason, we couldn't decode Unicode here.. silently discard the operation
+ pass
+ if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
+ try:
+ self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
+ except:
+ # For some reason, we couldn't decode Unicode here.. silently discard the operation
+ pass
+
+ # Parse Version to know the target Operating system name. Not provided elsewhere anymore
+ if ntlmChallenge.fields.has_key('Version'):
+ version = ntlmChallenge['Version']
+
+ if len(version) >= 4:
+ self._Session['ServerOS'] = "Windows %d.%d Build %d" % (ord(version[0]), ord(version[1]), struct.unpack('<H',version[2:4])[0])
+ self._Session["ServerOSMajor"] = ord(version[0])
+ self._Session["ServerOSMinor"] = ord(version[1])
+ self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0]
+
+ type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash)
+
+ if exportedSessionKey is not None:
+ self._Session['SessionKey'] = exportedSessionKey
+ if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
+ self._Session['SigningKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCMAC\x00", "SmbSign\x00", 128)
+
+ respToken2 = SPNEGO_NegTokenResp()
+ respToken2['ResponseToken'] = str(type3)
+
+ # Reusing the previous structure
+ sessionSetup['SecurityBufferLength'] = len(respToken2)
+ sessionSetup['Buffer'] = respToken2.getData()
+
+ packetID = self.sendSMB(packet)
+ packet = self.recvSMB(packetID)
+ try:
+ if packet.isValidAnswer(STATUS_SUCCESS):
+ sessionSetupResponse = SMB2SessionSetup_Response(packet['Data'])
+ self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags']
+
+ # Calculate the key derivations for dialect 3.0
+ if self._Session['SigningRequired'] is True:
+ self._Session['SigningActivated'] = True
+ if self._Connection['Dialect'] == SMB2_DIALECT_30:
+ self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2APP\x00", "SmbRpc\x00", 128)
+ self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerIn \x00", 128)
+ self._Session['DecryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerOut\x00", 128)
+
+ return True
+ except:
+ # We clean the stuff we used in case we want to authenticate again
+ # within the same connection
+ self._Session['UserCredentials'] = ''
+ self._Session['Connection'] = 0
+ self._Session['SessionID'] = 0
+ self._Session['SigningRequired'] = False
+ self._Session['SigningKey'] = ''
+ self._Session['SessionKey'] = ''
+ self._Session['SigningActivated'] = False
+ raise
+
+ def connectTree(self, share):
+
+ # Just in case this came with the full path (maybe an SMB1 client), let's just leave
+ # the sharename, we'll take care of the rest
+
+ #print self._Session['TreeConnectTable']
+ share = share.split('\\')[-1]
+ if self._Session['TreeConnectTable'].has_key(share):
+ # Already connected, no need to reconnect
+ treeEntry = self._Session['TreeConnectTable'][share]
+ treeEntry['NumberOfUses'] += 1
+ self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1
+ return treeEntry['TreeConnectId']
+
+ #path = share
+ try:
+ _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0]
+ remoteHost = sockaddr[0]
+ except:
+ remoteHost = self._Connection['ServerIP']
+ path = '\\\\' + remoteHost + '\\' +share
+
+ treeConnect = SMB2TreeConnect()
+ treeConnect['Buffer'] = path.encode('utf-16le')
+ treeConnect['PathLength'] = len(path)*2
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_TREE_CONNECT
+ packet['Data'] = treeConnect
+ packetID = self.sendSMB(packet)
+ packet = self.recvSMB(packetID)
+ if packet.isValidAnswer(STATUS_SUCCESS):
+ treeConnectResponse = SMB2TreeConnect_Response(packet['Data'])
+ treeEntry = copy.deepcopy(TREE_CONNECT)
+ treeEntry['ShareName'] = share
+ treeEntry['TreeConnectId'] = packet['TreeID']
+ treeEntry['Session'] = packet['SessionID']
+ treeEntry['NumberOfUses'] += 1
+ if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS:
+ treeEntry['IsDfsShare'] = True
+ if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY:
+ treeEntry['IsCAShare'] = True
+
+ if self._Connection['Dialect'] == SMB2_DIALECT_30:
+ if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA):
+ treeEntry['EncryptData'] = True
+ # ToDo: This and what follows
+ # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a
+ # decryption key as specified in section 3.1.4.2, by providing the following inputs and store
+ # them in Session.EncryptionKey and Session.DecryptionKey:
+ if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT:
+ treeEntry['IsScaleoutShare'] = True
+
+ self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry
+ self._Session['TreeConnectTable'][share] = treeEntry
+
+ return packet['TreeID']
+
+ def disconnectTree(self, treeId):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ if self._Session['TreeConnectTable'].has_key(treeId):
+ # More than 1 use? descrease it and return, if not, send the packet
+ if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
+ treeEntry = self._Session['TreeConnectTable'][treeId]
+ treeEntry['NumberOfUses'] -= 1
+ self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1
+ return True
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_TREE_DISCONNECT
+ packet['TreeID'] = treeId
+ treeDisconnect = SMB2TreeDisconnect()
+ packet['Data'] = treeDisconnect
+ packetID = self.sendSMB(packet)
+ packet = self.recvSMB(packetID)
+ if packet.isValidAnswer(STATUS_SUCCESS):
+ shareName = self._Session['TreeConnectTable'][treeId]['ShareName']
+ del(self._Session['TreeConnectTable'][shareName])
+ del(self._Session['TreeConnectTable'][treeId])
+ return True
+
+ def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ fileName = string.replace(fileName, '/', '\\')
+ if len(fileName) > 0:
+ fileName = ntpath.normpath(fileName)
+ if fileName[0] == '\\':
+ fileName = fileName[1:]
+
+ if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
+ pathName = fileName
+ else:
+ pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName
+
+ fileEntry = copy.deepcopy(FILE)
+ fileEntry['LeaseKey'] = uuid.generate()
+ fileEntry['LeaseState'] = SMB2_LEASE_NONE
+ self.GlobalFileTable[pathName] = fileEntry
+
+ if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
+ # Is this file NOT on the root directory?
+ if len(fileName.split('\\')) > 2:
+ parentDir = ntpath.dirname(pathName)
+ if self.GlobalFileTable.has_key(parentDir):
+ LOG.critical("Don't know what to do now! :-o")
+ raise
+ else:
+ parentEntry = copy.deepcopy(FILE)
+ parentEntry['LeaseKey'] = uuid.generate()
+ parentEntry['LeaseState'] = SMB2_LEASE_NONE
+ self.GlobalFileTable[parentDir] = parentEntry
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_CREATE
+ packet['TreeID'] = treeId
+ if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
+ packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS
+
+ smb2Create = SMB2Create()
+ smb2Create['SecurityFlags'] = 0
+ smb2Create['RequestedOplockLevel'] = oplockLevel
+ smb2Create['ImpersonationLevel'] = impersonationLevel
+ smb2Create['DesiredAccess'] = desiredAccess
+ smb2Create['FileAttributes'] = fileAttributes
+ smb2Create['ShareAccess'] = shareMode
+ smb2Create['CreateDisposition'] = creationDisposition
+ smb2Create['CreateOptions'] = creationOptions
+
+ smb2Create['NameLength'] = len(fileName)*2
+ if fileName != '':
+ smb2Create['Buffer'] = fileName.encode('utf-16le')
+ else:
+ smb2Create['Buffer'] = '\x00'
+
+ if createContexts is not None:
+ smb2Create['Buffer'] += createContexts
+ smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength']
+ smb2Create['CreateContextsLength'] = len(createContexts)
+ else:
+ smb2Create['CreateContextsOffset'] = 0
+ smb2Create['CreateContextsLength'] = 0
+
+ packet['Data'] = smb2Create
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ createResponse = SMB2Create_Response(ans['Data'])
+
+ openFile = copy.deepcopy(OPEN)
+ openFile['FileID'] = createResponse['FileID']
+ openFile['TreeConnect'] = treeId
+ openFile['Oplocklevel'] = oplockLevel
+ openFile['Durable'] = False
+ openFile['ResilientHandle'] = False
+ openFile['LastDisconnectTime'] = 0
+ openFile['FileName'] = pathName
+
+ # ToDo: Complete the OperationBuckets
+ if self._Connection['Dialect'] == SMB2_DIALECT_30:
+ openFile['DesiredAccess'] = oplockLevel
+ openFile['ShareMode'] = oplockLevel
+ openFile['CreateOptions'] = oplockLevel
+ openFile['FileAttributes'] = oplockLevel
+ openFile['CreateDisposition'] = oplockLevel
+
+ # ToDo: Process the contexts
+ self._Session['OpenTable'][str(createResponse['FileID'])] = openFile
+
+ # The client MUST generate a handle for the Open, and it MUST
+ # return success and the generated handle to the calling application.
+ # In our case, str(FileID)
+ return str(createResponse['FileID'])
+
+ def close(self, treeId, fileId):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_CLOSE
+ packet['TreeID'] = treeId
+
+ smbClose = SMB2Close()
+ smbClose['Flags'] = 0
+ smbClose['FileID'] = fileId
+
+ packet['Data'] = smbClose
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']])
+ del(self._Session['OpenTable'][fileId])
+
+ # ToDo Remove stuff from GlobalFileTable
+ return True
+
+ def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True):
+ # IMPORTANT NOTE: As you can see, this was coded as a recursive function
+ # Hence, you can exhaust the memory pretty easy ( large bytesToRead )
+ # This function should NOT be used for reading files directly, but another higher
+ # level function should be used that will break the read into smaller pieces
+
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_READ
+ packet['TreeID'] = treeId
+
+ if self._Connection['MaxReadSize'] < bytesToRead:
+ maxBytesToRead = self._Connection['MaxReadSize']
+ else:
+ maxBytesToRead = bytesToRead
+
+ if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
+ packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) / 65536)
+ else:
+ maxBytesToRead = min(65536,bytesToRead)
+
+ smbRead = SMB2Read()
+ smbRead['Padding'] = 0x50
+ smbRead['FileID'] = fileId
+ smbRead['Length'] = maxBytesToRead
+ smbRead['Offset'] = offset
+ packet['Data'] = smbRead
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ readResponse = SMB2Read_Response(ans['Data'])
+ retData = readResponse['Buffer']
+ if readResponse['DataRemaining'] > 0:
+ retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer)
+ return retData
+
+ def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True):
+ # IMPORTANT NOTE: As you can see, this was coded as a recursive function
+ # Hence, you can exhaust the memory pretty easy ( large bytesToWrite )
+ # This function should NOT be used for writing directly to files, but another higher
+ # level function should be used that will break the writes into smaller pieces
+
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_WRITE
+ packet['TreeID'] = treeId
+
+ if self._Connection['MaxWriteSize'] < bytesToWrite:
+ maxBytesToWrite = self._Connection['MaxWriteSize']
+ else:
+ maxBytesToWrite = bytesToWrite
+
+ if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
+ packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) / 65536)
+ else:
+ maxBytesToWrite = min(65536,bytesToWrite)
+
+ smbWrite = SMB2Write()
+ smbWrite['FileID'] = fileId
+ smbWrite['Length'] = maxBytesToWrite
+ smbWrite['Offset'] = offset
+ smbWrite['WriteChannelInfoOffset'] = 0
+ smbWrite['Buffer'] = data[:maxBytesToWrite]
+ packet['Data'] = smbWrite
+
+ packetID = self.sendSMB(packet)
+ if waitAnswer is True:
+ ans = self.recvSMB(packetID)
+ else:
+ return maxBytesToWrite
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ writeResponse = SMB2Write_Response(ans['Data'])
+ bytesWritten = writeResponse['Count']
+ if bytesWritten < bytesToWrite:
+ bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer)
+ return bytesWritten
+
+ def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_QUERY_DIRECTORY
+ packet['TreeID'] = treeId
+
+ queryDirectory = SMB2QueryDirectory()
+ queryDirectory['FileInformationClass'] = informationClass
+ if resumeIndex != 0 :
+ queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED
+ queryDirectory['FileIndex'] = resumeIndex
+ queryDirectory['FileID'] = fileId
+ if maxBufferSize is None:
+ maxBufferSize = self._Connection['MaxReadSize']
+ queryDirectory['OutputBufferLength'] = maxBufferSize
+ queryDirectory['FileNameLength'] = len(searchString)*2
+ queryDirectory['Buffer'] = searchString.encode('utf-16le')
+
+ packet['Data'] = queryDirectory
+
+ if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
+ packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) / 65536)
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data'])
+ return queryDirectoryResponse['Buffer']
+
+ def echo(self):
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_ECHO
+ smbEcho = SMB2Echo()
+ packet['Data'] = smbEcho
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ return True
+
+ def cancel(self, packetID):
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_CANCEL
+ packet['MessageID'] = packetID
+
+ smbCancel = SMB2Cancel()
+
+ packet['Data'] = smbCancel
+ self.sendSMB(packet)
+
+ def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if fileId is None:
+ fileId = '\xff'*16
+ else:
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_IOCTL
+ packet['TreeID'] = treeId
+
+ smbIoctl = SMB2Ioctl()
+ smbIoctl['FileID'] = fileId
+ smbIoctl['CtlCode'] = ctlCode
+ smbIoctl['MaxInputResponse'] = maxInputResponse
+ smbIoctl['MaxOutputResponse'] = maxOutputResponse
+ smbIoctl['InputCount'] = len(inputBlob)
+ if len(inputBlob) == 0:
+ smbIoctl['InputOffset'] = 0
+ smbIoctl['Buffer'] = '\x00'
+ else:
+ smbIoctl['Buffer'] = inputBlob
+ smbIoctl['OutputOffset'] = 0
+ smbIoctl['MaxOutputResponse'] = maxOutputResponse
+ smbIoctl['Flags'] = flags
+
+ packet['Data'] = smbIoctl
+
+ packetID = self.sendSMB(packet)
+
+ if waitAnswer == 0:
+ return True
+
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
+ return smbIoctlResponse['Buffer']
+
+ def flush(self,treeId, fileId):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_FLUSH
+ packet['TreeID'] = treeId
+
+ smbFlush = SMB2Flush()
+ smbFlush['FileID'] = fileId
+
+ packet['Data'] = smbFlush
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ return True
+
+ def lock(self, treeId, fileId, locks, lockSequence = 0):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_LOCK
+ packet['TreeID'] = treeId
+
+ smbLock = SMB2Lock()
+ smbLock['FileID'] = fileId
+ smbLock['LockCount'] = len(locks)
+ smbLock['LockSequence'] = lockSequence
+ smbLock['Locks'] = ''.join(str(x) for x in locks)
+
+ packet['Data'] = smbLock
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ smbFlushResponse = SMB2Lock_Response(ans['Data'])
+ return True
+
+ # ToDo:
+ # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST
+ # do the following:
+ # The client MUST scan through Open.OperationBuckets and find an element with its Free field
+ # set to TRUE. If no such element could be found, an implementation-specific error MUST be
+ # returned to the application.
+ # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and
+ # let BucketNumber = BucketIndex +1.
+ # Set Open.OperationBuckets[BucketIndex].Free = FALSE
+ # Let the SequenceNumber of the element chosen above be referred to as BucketSequence.
+ # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) +
+ # BucketSequence.
+ # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic.
+
+ def logoff(self):
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_LOGOFF
+
+ smbLogoff = SMB2Logoff()
+
+ packet['Data'] = smbLogoff
+
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ # We clean the stuff we used in case we want to authenticate again
+ # within the same connection
+ self._Session['UserCredentials'] = ''
+ self._Session['Connection'] = 0
+ self._Session['SessionID'] = 0
+ self._Session['SigningRequired'] = False
+ self._Session['SigningKey'] = ''
+ self._Session['SessionKey'] = ''
+ self._Session['SigningActivated'] = False
+ return True
+
+ def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_QUERY_INFO
+ packet['TreeID'] = treeId
+
+ queryInfo = SMB2QueryInfo()
+ queryInfo['FileID'] = fileId
+ queryInfo['InfoType'] = SMB2_0_INFO_FILE
+ queryInfo['FileInfoClass'] = fileInfoClass
+ queryInfo['OutputBufferLength'] = 65535
+ queryInfo['AdditionalInformation'] = additionalInformation
+ if len(inputBlob) == 0:
+ queryInfo['InputBufferOffset'] = 0
+ queryInfo['Buffer'] = '\x00'
+ else:
+ queryInfo['InputBufferLength'] = len(inputBlob)
+ queryInfo['Buffer'] = inputBlob
+ queryInfo['Flags'] = flags
+
+ packet['Data'] = queryInfo
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ queryResponse = SMB2QueryInfo_Response(ans['Data'])
+ return queryResponse['Buffer']
+
+ def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if self._Session['OpenTable'].has_key(fileId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ packet = self.SMB_PACKET()
+ packet['Command'] = SMB2_SET_INFO
+ packet['TreeID'] = treeId
+
+ setInfo = SMB2SetInfo()
+ setInfo['InfoType'] = SMB2_0_INFO_FILE
+ setInfo['FileInfoClass'] = fileInfoClass
+ setInfo['BufferLength'] = len(inputBlob)
+ setInfo['AdditionalInformation'] = additionalInformation
+ setInfo['FileID'] = fileId
+ setInfo['Buffer'] = inputBlob
+
+ packet['Data'] = setInfo
+ packetID = self.sendSMB(packet)
+ ans = self.recvSMB(packetID)
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ return True
+
+ def getSessionKey(self):
+ if self.getDialect() == SMB2_DIALECT_30:
+ return self._Session['ApplicationKey']
+ else:
+ return self._Session['SessionKey']
+
+ def setSessionKey(self, key):
+ if self.getDialect() == SMB2_DIALECT_30:
+ self._Session['ApplicationKey'] = key
+ else:
+ self._Session['SessionKey'] = key
+
+ ######################################################################
+ # Higher level functions
+
+ def rename(self, shareName, oldPath, newPath):
+ oldPath = string.replace(oldPath,'/', '\\')
+ oldPath = ntpath.normpath(oldPath)
+ if len(oldPath) > 0 and oldPath[0] == '\\':
+ oldPath = oldPath[1:]
+
+ newPath = string.replace(newPath,'/', '\\')
+ newPath = ntpath.normpath(newPath)
+ if len(newPath) > 0 and newPath[0] == '\\':
+ newPath = newPath[1:]
+
+ treeId = self.connectTree(shareName)
+ fileId = None
+ try:
+ fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0)
+ renameReq = FILE_RENAME_INFORMATION_TYPE_2()
+ renameReq['ReplaceIfExists'] = 1
+ renameReq['RootDirectory'] = '\x00'*8
+ renameReq['FileNameLength'] = len(newPath)*2
+ renameReq['FileName'] = newPath.encode('utf-16le')
+ self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO)
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ return True
+
+ def writeFile(self, treeId, fileId, data, offset = 0):
+ finished = False
+ writeOffset = offset
+ while not finished:
+ if len(data) == 0:
+ break
+ writeData = data[:self._Connection['MaxWriteSize']]
+ data = data[self._Connection['MaxWriteSize']:]
+ written = self.write(treeId, fileId, writeData, writeOffset, len(writeData))
+ writeOffset += written
+ return writeOffset - offset
+
+ def listPath(self, shareName, path, password = None):
+ # ToDo: Handle situations where share is password protected
+ path = string.replace(path,'/', '\\')
+ path = ntpath.normpath(path)
+ if len(path) > 0 and path[0] == '\\':
+ path = path[1:]
+
+ treeId = self.connectTree(shareName)
+
+ fileId = None
+ try:
+ # ToDo, we're assuming it's a directory, we should check what the file type is
+ fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0)
+ res = ''
+ files = []
+ from impacket import smb
+ while True:
+ try:
+ res = self.queryDirectory( treeId, fileId, ntpath.basename(path), maxBufferSize = 65535, informationClass = FILE_FULL_DIRECTORY_INFORMATION )
+ nextOffset = 1
+ while nextOffset != 0:
+ fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE)
+ fileInfo.fromString(res)
+ files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le')))
+ nextOffset = fileInfo['NextEntryOffset']
+ res = res[nextOffset:]
+ except SessionError, e:
+ if (e.get_error_code()) != STATUS_NO_MORE_FILES:
+ raise
+ break
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ return files
+
+ def mkdir(self, shareName, pathName, password = None):
+ # ToDo: Handle situations where share is password protected
+ pathName = string.replace(pathName,'/', '\\')
+ pathName = ntpath.normpath(pathName)
+ if len(pathName) > 0 and pathName[0] == '\\':
+ pathName = pathName[1:]
+
+ treeId = self.connectTree(shareName)
+
+ fileId = None
+ try:
+ fileId = self.create(treeId, pathName,GENERIC_ALL ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ return True
+
+ def rmdir(self, shareName, pathName, password = None):
+ # ToDo: Handle situations where share is password protected
+ pathName = string.replace(pathName,'/', '\\')
+ pathName = ntpath.normpath(pathName)
+ if len(pathName) > 0 and pathName[0] == '\\':
+ pathName = pathName[1:]
+
+ treeId = self.connectTree(shareName)
+
+ fileId = None
+ try:
+ fileId = self.create(treeId, pathName, DELETE, FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ return True
+
+ def remove(self, shareName, pathName, password = None):
+ # ToDo: Handle situations where share is password protected
+ pathName = string.replace(pathName,'/', '\\')
+ pathName = ntpath.normpath(pathName)
+ if len(pathName) > 0 and pathName[0] == '\\':
+ pathName = pathName[1:]
+
+ treeId = self.connectTree(shareName)
+
+ fileId = None
+ try:
+ fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ return True
+
+ def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
+ # ToDo: Handle situations where share is password protected
+ path = string.replace(path,'/', '\\')
+ path = ntpath.normpath(path)
+ if len(path) > 0 and path[0] == '\\':
+ path = path[1:]
+
+ treeId = self.connectTree(shareName)
+ fileId = None
+ from impacket import smb
+ try:
+ fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
+ res = self.queryInfo(treeId, fileId)
+ fileInfo = smb.SMBQueryFileStandardInfo(res)
+ fileSize = fileInfo['EndOfFile']
+ if (fileSize-offset) < self._Connection['MaxReadSize']:
+ # Skip reading 0 bytes files.
+ if (fileSize-offset) > 0:
+ data = self.read(treeId, fileId, offset, fileSize-offset)
+ callback(data)
+ else:
+ written = 0
+ toBeRead = fileSize-offset
+ while written < toBeRead:
+ data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize'])
+ written += len(data)
+ offset += len(data)
+ callback(data)
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE):
+ # ToDo: Handle situations where share is password protected
+ path = string.replace(path,'/', '\\')
+ path = ntpath.normpath(path)
+ if len(path) > 0 and path[0] == '\\':
+ path = path[1:]
+
+ treeId = self.connectTree(shareName)
+ fileId = None
+ try:
+ fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
+ finished = False
+ writeOffset = offset
+ while not finished:
+ data = callback(self._Connection['MaxWriteSize'])
+ if len(data) == 0:
+ break
+ written = self.write(treeId, fileId, data, writeOffset, len(data))
+ writeOffset += written
+ finally:
+ if fileId is not None:
+ self.close(treeId, fileId)
+ self.disconnectTree(treeId)
+
+ def waitNamedPipe(self, treeId, pipename, timeout = 5):
+ pipename = ntpath.basename(pipename)
+ if self._Session['TreeConnectTable'].has_key(treeId) is False:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+ if len(pipename) > 0xffff:
+ raise SessionError(STATUS_INVALID_PARAMETER)
+
+ pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()
+ pipeWait['Timeout'] = timeout*100000
+ pipeWait['NameLength'] = len(pipename)*2
+ pipeWait['TimeoutSpecified'] = 1
+ pipeWait['Name'] = pipename.encode('utf-16le')
+
+ return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0)
+
+ def getIOCapabilities(self):
+ res = dict()
+
+ res['MaxReadSize'] = self._Connection['MaxReadSize']
+ res['MaxWriteSize'] = self._Connection['MaxWriteSize']
+ return res
+
+
+ ######################################################################
+ # Backward compatibility functions and alias for SMB1 and DCE Transports
+ # NOTE: It is strongly recommended not to use these commands
+ # when implementing new client calls.
+ get_server_name = getServerName
+ get_server_domain = getServerDomain
+ get_server_dns_domain_name = getServerDNSDomainName
+ get_remote_name = getServerName
+ get_remote_host = getServerIP
+ get_server_os = getServerOS
+ get_server_os_major = getServerOSMajor
+ get_server_os_minor = getServerOSMinor
+ get_server_os_build = getServerOSBuild
+ tree_connect_andx = connectTree
+ tree_connect = connectTree
+ connect_tree = connectTree
+ disconnect_tree = disconnectTree
+ set_timeout = setTimeout
+ use_timeout = useTimeout
+ stor_file = storeFile
+ retr_file = retrieveFile
+ list_path = listPath
+
+ def __del__(self):
+ if self._NetBIOSSession:
+ self._NetBIOSSession.close()
+
+
+ def doesSupportNTLMv2(self):
+ # Always true :P
+ return True
+
+ def is_login_required(self):
+ # Always true :P
+ return True
+
+ def is_signing_required(self):
+ return self._Session["SigningRequired"]
+
+ def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None):
+ if len(fileName) > 0 and fileName[0] == '\\':
+ fileName = fileName[1:]
+
+ if cmd is not None:
+ from impacket import smb
+ ntCreate = smb.SMBCommand(data = str(cmd))
+ params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters'])
+ return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'],
+ params['CreateOptions'], params['Disposition'], params['FileAttributes'],
+ params['Impersonation'], params['SecurityFlags'])
+
+ else:
+ return self.create(treeId, fileName,
+ FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
+ FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )
+
+ def get_socket(self):
+ return self._NetBIOSSession.get_socket()
+
+
+ def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
+ # ToDo: Handle the custom smb_packet situation
+ return self.write(tid, fid, data, offset, len(data))
+
+ def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0):
+ return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer)
+
+ def TransactNamedPipeRecv(self):
+ ans = self.recvSMB()
+
+ if ans.isValidAnswer(STATUS_SUCCESS):
+ smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
+ return smbIoctlResponse['Buffer']
+
+
+ def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
+ # ToDo: Handle the custom smb_packet situation
+ if max_size is None:
+ max_size = self._Connection['MaxReadSize']
+ return self.read(tid, fid, offset, max_size, wait_answer)
+
+ def list_shared(self):
+ # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED
+ raise SessionError(STATUS_NOT_IMPLEMENTED)
+
+ def open_andx(self, tid, fileName, open_mode, desired_access):
+ # ToDo Return all the attributes of the file
+ if len(fileName) > 0 and fileName[0] == '\\':
+ fileName = fileName[1:]
+
+ fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0)
+ return fileId, 0, 0, 0, 0, 0, 0, 0, 0
+