Coverage for /root/GitHubProjects/impacket/impacket/smb3.py : 69%

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
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Author: Alberto Solino (@agsolino)
8#
9# Description:
10# [MS-SMB2] Protocol Implementation (SMB2 and SMB3)
11# As you might see in the code, it's implemented strictly following
12# the structures defined in the protocol specification. This may
13# not be the most efficient way (e.g. self._Connection is the
14# same to self._Session in the context of this library ) but
15# it certainly helps following the document way easier.
16#
17# ToDo:
18# [X] Implement SMB2_CHANGE_NOTIFY
19# [X] Implement SMB2_QUERY_INFO
20# [X] Implement SMB2_SET_INFO
21# [ ] Implement SMB2_OPLOCK_BREAK
22# [X] Implement SMB3 signing
23# [X] Implement SMB3 encryption
24# [ ] Add more backward compatible commands from the smb.py code
25# [ ] Fix up all the 'ToDo' comments inside the code
26#
27from __future__ import division
28from __future__ import print_function
30import socket
31import ntpath
32import random
33import string
34import struct
35from six import indexbytes, b
36from binascii import a2b_hex
37from contextlib import contextmanager
38from pyasn1.type.univ import noValue
39from Cryptodome.Cipher import AES
41from impacket import nmb, ntlm, uuid, crypto
42from impacket.smb3structs import *
43from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \
44 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES
45from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID
46from impacket.krb5.gssapi import KRB5_AP_REQ
49# For signing
50import hashlib, hmac, copy
52# Our random number generator
53try:
54 rand = random.SystemRandom()
55except NotImplementedError:
56 rand = random
57 pass
59# Structs to be used
60TREE_CONNECT = {
61 'ShareName' : '',
62 'TreeConnectId' : 0,
63 'Session' : 0,
64 'IsDfsShare' : False,
65 # If the client implements the SMB 3.0 dialect,
66 # the client MUST also implement the following
67 'IsCAShare' : False,
68 'EncryptData' : False,
69 'IsScaleoutShare' : False,
70 # Outside the protocol
71 'NumberOfUses' : 0,
72}
74FILE = {
75 'OpenTable' : [],
76 'LeaseKey' : '',
77 'LeaseState' : 0,
78 'LeaseEpoch' : 0,
79}
81OPEN = {
82 'FileID' : '',
83 'TreeConnect' : 0,
84 'Connection' : 0, # Not Used
85 'Oplocklevel' : 0,
86 'Durable' : False,
87 'FileName' : '',
88 'ResilientHandle' : False,
89 'LastDisconnectTime' : 0,
90 'ResilientTimeout' : 0,
91 'OperationBuckets' : [],
92 # If the client implements the SMB 3.0 dialect,
93 # the client MUST implement the following
94 'CreateGuid' : '',
95 'IsPersistent' : False,
96 'DesiredAccess' : '',
97 'ShareMode' : 0,
98 'CreateOption' : '',
99 'FileAttributes' : '',
100 'CreateDisposition' : '',
101}
103REQUEST = {
104 'CancelID' : '',
105 'Message' : '',
106 'Timestamp' : 0,
107}
109CHANNEL = {
110 'SigningKey' : '',
111 'Connection' : 0,
112}
115class SessionError(Exception):
116 def __init__( self, error = 0, packet=0):
117 Exception.__init__(self)
118 self.error = error
119 self.packet = packet
121 def get_error_code( self ):
122 return self.error
124 def get_error_packet( self ):
125 return self.packet
127 def __str__( self ):
128 return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error])
131class SMB3:
132 class HostnameValidationException(Exception):
133 pass
135 def __init__(self, remote_name, remote_host, my_name=None, host_type=nmb.TYPE_SERVER, sess_port=445, timeout=60,
136 UDP=0, preferredDialect=None, session=None, negSessionResponse=None):
138 # [MS-SMB2] Section 3
139 self.RequireMessageSigning = False #
140 self.ConnectionTable = {}
141 self.GlobalFileTable = {}
142 self.ClientGuid = ''.join([random.choice(string.ascii_letters) for i in range(16)])
143 # Only for SMB 3.0
144 self.EncryptionAlgorithmList = ['AES-CCM']
145 self.MaxDialect = []
146 self.RequireSecureNegotiate = False
148 # Per Transport Connection Data
149 self._Connection = {
150 # Indexed by SessionID
151 #'SessionTable' : {},
152 # Indexed by MessageID
153 'OutstandingRequests' : {},
154 'OutstandingResponses' : {}, #
155 'SequenceWindow' : 0, #
156 'GSSNegotiateToken' : '', #
157 'MaxTransactSize' : 0, #
158 'MaxReadSize' : 0, #
159 'MaxWriteSize' : 0, #
160 'ServerGuid' : '', #
161 'RequireSigning' : False, #
162 'ServerName' : '', #
163 # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST
164 # also implement the following
165 'Dialect' : 0, #
166 'SupportsFileLeasing' : False, #
167 'SupportsMultiCredit' : False, #
168 # If the client implements the SMB 3.0 dialect,
169 # it MUST also implement the following
170 'SupportsDirectoryLeasing' : False, #
171 'SupportsMultiChannel' : False, #
172 'SupportsPersistentHandles': False, #
173 'SupportsEncryption' : False, #
174 'ClientCapabilities' : 0,
175 'ServerCapabilities' : 0, #
176 'ClientSecurityMode' : 0, #
177 'ServerSecurityMode' : 0, #
178 # Outside the protocol
179 'ServerIP' : '', #
180 'ClientName' : '', #
181 #GSSoptions (MutualAuth and Delegate)
182 'GSSoptions' : {},
183 'PreauthIntegrityHashValue': a2b_hex(b'0'*128)
184 }
186 self._Session = {
187 'SessionID' : 0, #
188 'TreeConnectTable' : {}, #
189 'SessionKey' : b'', #
190 'SigningRequired' : False, #
191 'Connection' : 0, #
192 'UserCredentials' : '', #
193 'OpenTable' : {}, #
194 # If the client implements the SMB 3.0 dialect,
195 # it MUST also implement the following
196 'ChannelList' : [],
197 'ChannelSequence' : 0,
198 #'EncryptData' : False,
199 'EncryptData' : True,
200 'EncryptionKey' : '',
201 'DecryptionKey' : '',
202 'SigningKey' : '',
203 'ApplicationKey' : b'',
204 # Outside the protocol
205 'SessionFlags' : 0, #
206 'ServerName' : '', #
207 'ServerDomain' : '', #
208 'ServerDNSDomainName' : '', #
209 'ServerDNSHostName' : '', #
210 'ServerOS' : '', #
211 'SigningActivated' : False, #
212 'PreauthIntegrityHashValue': a2b_hex(b'0'*128),
213 'CalculatePreAuthHash' : True,
214 }
216 self.SMB_PACKET = SMB2Packet
218 self._timeout = timeout
219 self._Connection['ServerIP'] = remote_host
220 self._NetBIOSSession = None
221 self._preferredDialect = preferredDialect
222 self._doKerberos = False
224 # Strict host validation - off by default
225 self._strict_hostname_validation = False
226 self._validation_allow_absent = True
227 self._accepted_hostname = ''
229 self.__userName = ''
230 self.__password = ''
231 self.__domain = ''
232 self.__lmhash = ''
233 self.__nthash = ''
234 self.__kdc = ''
235 self.__aesKey = ''
236 self.__TGT = None
237 self.__TGS = None
239 if sess_port == 445 and remote_name == '*SMBSERVER': 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true
240 self._Connection['ServerName'] = remote_host
241 else:
242 self._Connection['ServerName'] = remote_name
244 # This is on purpose. I'm still not convinced to do a socket.gethostname() if not specified
245 if my_name is None: 245 ↛ 248line 245 didn't jump to line 248, because the condition on line 245 was never false
246 self._Connection['ClientName'] = ''
247 else:
248 self._Connection['ClientName'] = my_name
250 if session is None:
251 if not my_name: 251 ↛ 258line 251 didn't jump to line 258, because the condition on line 251 was never false
252 # If destination port is 139 yes, there's some client disclosure
253 my_name = socket.gethostname()
254 i = my_name.find('.')
255 if i > -1: 255 ↛ 256line 255 didn't jump to line 256, because the condition on line 255 was never true
256 my_name = my_name[:i]
258 if UDP: 258 ↛ 259line 258 didn't jump to line 259, because the condition on line 258 was never true
259 self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
260 else:
261 self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
263 self.negotiateSession(preferredDialect)
264 else:
265 self._NetBIOSSession = session
266 # We should increase the SequenceWindow since a packet was already received.
267 self._Connection['SequenceWindow'] += 1
268 # Let's negotiate again if needed (or parse the existing response) using the same connection
269 self.negotiateSession(preferredDialect, negSessionResponse)
271 def printStatus(self):
272 print("CONNECTION")
273 for i in list(self._Connection.items()):
274 print("%-40s : %s" % i)
275 print()
276 print("SESSION")
277 for i in list(self._Session.items()):
278 print("%-40s : %s" % i)
280 def __UpdateConnectionPreAuthHash(self, data):
281 from Cryptodome.Hash import SHA512
282 calculatedHash = SHA512.new()
283 calculatedHash.update(self._Connection['PreauthIntegrityHashValue'])
284 calculatedHash.update(data)
285 self._Connection['PreauthIntegrityHashValue'] = calculatedHash.digest()
287 def __UpdatePreAuthHash(self, data):
288 from Cryptodome.Hash import SHA512
289 calculatedHash = SHA512.new()
290 calculatedHash.update(self._Session['PreauthIntegrityHashValue'])
291 calculatedHash.update(data)
292 self._Session['PreauthIntegrityHashValue'] = calculatedHash.digest()
294 def getKerberos(self):
295 return self._doKerberos
297 def getServerName(self):
298 return self._Session['ServerName']
300 def getClientName(self):
301 return self._Session['ClientName']
303 def getRemoteName(self):
304 if self._Session['ServerName'] == '': 304 ↛ 305line 304 didn't jump to line 305, because the condition on line 304 was never true
305 return self._Connection['ServerName']
306 return self._Session['ServerName']
308 def setRemoteName(self, name):
309 self._Session['ServerName'] = name
310 return True
312 def getServerIP(self):
313 return self._Connection['ServerIP']
315 def getServerDomain(self):
316 return self._Session['ServerDomain']
318 def getServerDNSDomainName(self):
319 return self._Session['ServerDNSDomainName']
321 def getServerDNSHostName(self):
322 return self._Session['ServerDNSHostName']
324 def getServerOS(self):
325 return self._Session['ServerOS']
327 def getServerOSMajor(self):
328 return self._Session['ServerOSMajor']
330 def getServerOSMinor(self):
331 return self._Session['ServerOSMinor']
333 def getServerOSBuild(self):
334 return self._Session['ServerOSBuild']
336 def isGuestSession(self):
337 return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST
339 def setTimeout(self, timeout):
340 self._timeout = timeout
342 @contextmanager
343 def useTimeout(self, timeout):
344 prev_timeout = self.getTimeout(timeout)
345 try:
346 yield
347 finally:
348 self.setTimeout(prev_timeout)
350 def getDialect(self):
351 return self._Connection['Dialect']
353 def signSMB(self, packet):
354 packet['Signature'] = '\x00'*16
355 if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002:
356 if len(self._Session['SessionKey']) > 0: 356 ↛ exitline 356 didn't return from function 'signSMB', because the condition on line 356 was never false
357 signature = hmac.new(self._Session['SessionKey'], packet.getData(), hashlib.sha256).digest()
358 packet['Signature'] = signature[:16]
359 else:
360 if len(self._Session['SessionKey']) > 0: 360 ↛ exitline 360 didn't return from function 'signSMB', because the condition on line 360 was never false
361 p = packet.getData()
362 signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p))
363 packet['Signature'] = signature
365 def sendSMB(self, packet):
366 # The idea here is to receive multiple/single commands and create a compound request, and send it
367 # Should return the MessageID for later retrieval. Implement compounded related requests.
369 # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or
370 # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the
371 # SMB2 header to Session.ChannelSequence
373 # Check this is not a CANCEL request. If so, don't consume sequence numbers
374 if packet['Command'] is not SMB2_CANCEL: 374 ↛ 377line 374 didn't jump to line 377, because the condition on line 374 was never false
375 packet['MessageID'] = self._Connection['SequenceWindow']
376 self._Connection['SequenceWindow'] += 1
377 packet['SessionID'] = self._Session['SessionID']
379 # Default the credit charge to 1 unless set by the caller
380 if ('CreditCharge' in packet.fields) is False:
381 packet['CreditCharge'] = 1
383 # Standard credit request after negotiating protocol
384 if self._Connection['SequenceWindow'] > 3:
385 packet['CreditRequestResponse'] = 127
387 messageId = packet['MessageID']
389 if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
390 if packet['TreeID'] > 0 and (packet['TreeID'] in self._Session['TreeConnectTable']) is True:
391 if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False: 391 ↛ 398line 391 didn't jump to line 398, because the condition on line 391 was never false
392 packet['Flags'] = SMB2_FLAGS_SIGNED
393 self.signSMB(packet)
394 elif packet['TreeID'] == 0: 394 ↛ 398line 394 didn't jump to line 398, because the condition on line 394 was never false
395 packet['Flags'] = SMB2_FLAGS_SIGNED
396 self.signSMB(packet)
398 if packet['Command'] is SMB2_NEGOTIATE:
399 data = packet.getData()
400 self.__UpdateConnectionPreAuthHash(data)
401 self._Session['CalculatePreAuthHash'] = False
403 if packet['Command'] is SMB2_SESSION_SETUP:
404 self._Session['CalculatePreAuthHash'] = True
406 if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True):
407 plainText = packet.getData()
408 transformHeader = SMB2_TRANSFORM_HEADER()
409 transformHeader['Nonce'] = ''.join([rand.choice(string.ascii_letters) for _ in range(11)])
410 transformHeader['OriginalMessageSize'] = len(plainText)
411 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM
412 transformHeader['SessionID'] = self._Session['SessionID']
413 cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, b(transformHeader['Nonce']))
414 cipher.update(transformHeader.getData()[20:])
415 cipherText = cipher.encrypt(plainText)
416 transformHeader['Signature'] = cipher.digest()
417 packet = transformHeader.getData() + cipherText
419 self._NetBIOSSession.send_packet(packet)
420 else:
421 data = packet.getData()
422 if self._Session['CalculatePreAuthHash'] is True:
423 self.__UpdatePreAuthHash(data)
425 self._NetBIOSSession.send_packet(data)
427 return messageId
429 def recvSMB(self, packetID = None):
430 # First, verify we don't have the packet already
431 if packetID in self._Connection['OutstandingResponses']: 431 ↛ 432line 431 didn't jump to line 432, because the condition on line 431 was never true
432 return self._Connection['OutstandingResponses'].pop(packetID)
434 data = self._NetBIOSSession.recv_packet(self._timeout)
436 if data.get_trailer().startswith(b'\xfdSMB'):
437 # Packet is encrypted
438 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
439 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
440 cipher.update(transformHeader.getData()[20:])
441 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
442 #cipher.verify(transformHeader['Signature'])
443 packet = SMB2Packet(plainText)
444 else:
445 # In all SMB dialects for a response this field is interpreted as the Status field.
446 # This field can be set to any value. For a list of valid status codes,
447 # see [MS-ERREF] section 2.3.
448 packet = SMB2Packet(data.get_trailer())
450 # Loop while we receive pending requests
451 if packet['Status'] == STATUS_PENDING:
452 status = STATUS_PENDING
453 while status == STATUS_PENDING:
454 data = self._NetBIOSSession.recv_packet(self._timeout)
455 if data.get_trailer().startswith(b'\xfeSMB'): 455 ↛ 456line 455 didn't jump to line 456, because the condition on line 455 was never true
456 packet = SMB2Packet(data.get_trailer())
457 else:
458 # Packet is encrypted
459 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
460 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
461 cipher.update(transformHeader.getData()[20:])
462 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
463 #cipher.verify(transformHeader['Signature'])
464 packet = SMB2Packet(plainText)
465 status = packet['Status']
467 if packet['MessageID'] == packetID or packetID is None: 467 ↛ 476line 467 didn't jump to line 476, because the condition on line 467 was never false
468 # Let's update the sequenceWindow based on the CreditsCharged
469 # In the SMB 2.0.2 dialect, this field MUST NOT be used and MUST be reserved.
470 # The sender MUST set this to 0, and the receiver MUST ignore it.
471 # In all other dialects, this field indicates the number of credits that this request consumes.
472 if self._Connection['Dialect'] > SMB2_DIALECT_002:
473 self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1)
474 return packet
475 else:
476 self._Connection['OutstandingResponses'][packet['MessageID']] = packet
477 return self.recvSMB(packetID)
479 def negotiateSession(self, preferredDialect = None, negSessionResponse = None):
480 # Let's store some data for later use
481 self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
482 if self.RequireMessageSigning is True: 482 ↛ 483line 482 didn't jump to line 483, because the condition on line 482 was never true
483 self._Connection['ClientSecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED
484 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
485 currentDialect = SMB2_DIALECT_WILDCARD
487 # Do we have a negSessionPacket already?
488 if negSessionResponse is not None:
489 # Yes, let's store the dialect answered back
490 negResp = SMB2Negotiate_Response(negSessionResponse['Data'])
491 currentDialect = negResp['DialectRevision']
493 if currentDialect == SMB2_DIALECT_WILDCARD: 493 ↛ 563line 493 didn't jump to line 563, because the condition on line 493 was never false
494 # Still don't know the chosen dialect, let's send our options
496 packet = self.SMB_PACKET()
497 packet['Command'] = SMB2_NEGOTIATE
498 negSession = SMB2Negotiate()
500 negSession['SecurityMode'] = self._Connection['ClientSecurityMode']
501 negSession['Capabilities'] = self._Connection['Capabilities']
502 negSession['ClientGuid'] = self.ClientGuid
503 if preferredDialect is not None:
504 negSession['Dialects'] = [preferredDialect]
505 if preferredDialect == SMB2_DIALECT_311: 505 ↛ 507line 505 didn't jump to line 507, because the condition on line 505 was never true
506 # Build the Contexts
507 contextData = SMB311ContextData()
508 contextData['NegotiateContextOffset'] = 64+38+2
509 contextData['NegotiateContextCount'] = 0
510 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_PREAUTH_INTEGRITY_CAPABILITIES
511 # to the negotiate request as specified in section 2.2.3.1:
512 negotiateContext = SMB2NegotiateContext()
513 negotiateContext['ContextType'] = SMB2_PREAUTH_INTEGRITY_CAPABILITIES
515 preAuthIntegrityCapabilities = SMB2PreAuthIntegrityCapabilities()
516 preAuthIntegrityCapabilities['HashAlgorithmCount'] = 1
517 preAuthIntegrityCapabilities['SaltLength'] = 32
518 preAuthIntegrityCapabilities['HashAlgorithms'] = b'\x01\x00'
519 preAuthIntegrityCapabilities['Salt'] = ''.join([rand.choice(string.ascii_letters) for _ in
520 range(preAuthIntegrityCapabilities['SaltLength'])])
522 negotiateContext['Data'] = preAuthIntegrityCapabilities.getData()
523 negotiateContext['DataLength'] = len(negotiateContext['Data'])
524 contextData['NegotiateContextCount'] += 1
525 pad = b'\xFF' * ((8 - (negotiateContext['DataLength'] % 8)) % 8)
527 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_ENCRYPTION_CAPABILITIES
528 # to the negotiate request as specified in section 2.2.3.1 and initialize
529 # the Ciphers field with the ciphers supported by the client in the order of preference.
531 negotiateContext2 = SMB2NegotiateContext ()
532 negotiateContext2['ContextType'] = SMB2_ENCRYPTION_CAPABILITIES
534 encryptionCapabilities = SMB2EncryptionCapabilities()
535 encryptionCapabilities['CipherCount'] = 1
536 encryptionCapabilities['Ciphers'] = 1
538 negotiateContext2['Data'] = encryptionCapabilities.getData()
539 negotiateContext2['DataLength'] = len(negotiateContext2['Data'])
540 contextData['NegotiateContextCount'] += 1
542 negSession['ClientStartTime'] = contextData.getData()
543 negSession['Padding'] = b'\xFF\xFF'
544 # Subsequent negotiate contexts MUST appear at the first 8-byte aligned offset following the
545 # previous negotiate context.
546 negSession['NegotiateContextList'] = negotiateContext.getData() + pad + negotiateContext2.getData()
548 # Do you want to enforce encryption? Uncomment here:
549 #self._Connection['SupportsEncryption'] = True
551 else:
552 negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]
553 negSession['DialectCount'] = len(negSession['Dialects'])
554 packet['Data'] = negSession
556 packetID = self.sendSMB(packet)
557 ans = self.recvSMB(packetID)
558 if ans.isValidAnswer(STATUS_SUCCESS): 558 ↛ 563line 558 didn't jump to line 563, because the condition on line 558 was never false
559 negResp = SMB2Negotiate_Response(ans['Data'])
560 if negResp['DialectRevision'] == SMB2_DIALECT_311: 560 ↛ 561line 560 didn't jump to line 561, because the condition on line 560 was never true
561 self.__UpdateConnectionPreAuthHash(ans.rawData)
563 self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize'])
564 self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize'])
565 self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize'])
566 self._Connection['ServerGuid'] = negResp['ServerGuid']
567 self._Connection['GSSNegotiateToken'] = negResp['Buffer']
568 self._Connection['Dialect'] = negResp['DialectRevision']
569 if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED or \ 569 ↛ 572line 569 didn't jump to line 572, because the condition on line 569 was never false
570 self._Connection['Dialect'] == SMB2_DIALECT_311:
571 self._Connection['RequireSigning'] = True
572 if self._Connection['Dialect'] == SMB2_DIALECT_311: 572 ↛ 574line 572 didn't jump to line 574, because the condition on line 572 was never true
573 # Always Sign
574 self._Connection['RequireSigning'] = True
576 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING:
577 self._Connection['SupportsFileLeasing'] = True
578 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU:
579 self._Connection['SupportsMultiCredit'] = True
581 if self._Connection['Dialect'] >= SMB2_DIALECT_30:
582 # Switching to the right packet format
583 self.SMB_PACKET = SMB3Packet
584 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING: 584 ↛ 585line 584 didn't jump to line 585, because the condition on line 584 was never true
585 self._Connection['SupportsDirectoryLeasing'] = True
586 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL: 586 ↛ 587line 586 didn't jump to line 587, because the condition on line 586 was never true
587 self._Connection['SupportsMultiChannel'] = True
588 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES: 588 ↛ 589line 588 didn't jump to line 589, because the condition on line 588 was never true
589 self._Connection['SupportsPersistentHandles'] = True
590 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION: 590 ↛ 593line 590 didn't jump to line 593, because the condition on line 590 was never false
591 self._Connection['SupportsEncryption'] = True
593 self._Connection['ServerCapabilities'] = negResp['Capabilities']
594 self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
596 def getCredentials(self):
597 return (
598 self.__userName,
599 self.__password,
600 self.__domain,
601 self.__lmhash,
602 self.__nthash,
603 self.__aesKey,
604 self.__TGT,
605 self.__TGS)
607 def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None, mutualAuth=False):
608 # If TGT or TGS are specified, they are in the form of:
609 # TGS['KDC_REP'] = the response from the server
610 # TGS['cipher'] = the cipher used
611 # TGS['sessionKey'] = the sessionKey
612 # If we have hashes, normalize them
613 if lmhash != '' or nthash != '':
614 if len(lmhash) % 2: lmhash = '0%s' % lmhash
615 if len(nthash) % 2: nthash = '0%s' % nthash
616 try: # just in case they were converted already
617 lmhash = a2b_hex(lmhash)
618 nthash = a2b_hex(nthash)
619 except:
620 pass
622 self.__userName = user
623 self.__password = password
624 self.__domain = domain
625 self.__lmhash = lmhash
626 self.__nthash = nthash
627 self.__kdc = kdcHost
628 self.__aesKey = aesKey
629 self.__TGT = TGT
630 self.__TGS = TGS
631 self._doKerberos= True
633 sessionSetup = SMB2SessionSetup()
634 if self.RequireMessageSigning is True: 634 ↛ 635line 634 didn't jump to line 635, because the condition on line 634 was never true
635 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
636 else:
637 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
639 sessionSetup['Flags'] = 0
640 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
642 # Importing down here so pyasn1 is not required if kerberos is not used.
643 from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
644 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
645 from impacket.krb5 import constants
646 from impacket.krb5.types import Principal, KerberosTime, Ticket
647 from pyasn1.codec.der import decoder, encoder
648 import datetime
650 # First of all, we need to get a TGT for the user
651 userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
652 if TGT is None: 652 ↛ 656line 652 didn't jump to line 656, because the condition on line 652 was never false
653 if TGS is None: 653 ↛ 676line 653 didn't jump to line 676, because the condition on line 653 was never false
654 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
655 else:
656 tgt = TGT['KDC_REP']
657 cipher = TGT['cipher']
658 sessionKey = TGT['sessionKey']
660 # Save the ticket
661 # If you want, for debugging purposes
662# from impacket.krb5.ccache import CCache
663# ccache = CCache()
664# try:
665# if TGS is None:
666# ccache.fromTGT(tgt, oldSessionKey, sessionKey)
667# else:
668# ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] )
669# ccache.saveFile('/tmp/ticket.bin')
670# except Exception, e:
671# print e
672# pass
674 # Now that we have the TGT, we should ask for a TGS for cifs
676 if TGS is None: 676 ↛ 680line 676 didn't jump to line 680, because the condition on line 676 was never false
677 serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value)
678 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
679 else:
680 tgs = TGS['KDC_REP']
681 cipher = TGS['cipher']
682 sessionKey = TGS['sessionKey']
684 # Let's build a NegTokenInit with a Kerberos REQ_AP
686 blob = SPNEGO_NegTokenInit()
688 # Kerberos
689 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
691 # Let's extract the ticket from the TGS
692 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
693 ticket = Ticket()
694 ticket.from_asn1(tgs['ticket'])
696 # Now let's build the AP_REQ
697 apReq = AP_REQ()
698 apReq['pvno'] = 5
699 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
701 #Handle mutual authentication
702 opts = list()
704 if mutualAuth == True: 704 ↛ 705line 704 didn't jump to line 705, because the condition on line 704 was never true
705 from impacket.krb5.constants import APOptions
706 opts.append(constants.APOptions.mutual_required.value)
708 apReq['ap-options'] = constants.encodeFlags(opts)
709 seq_set(apReq,'ticket', ticket.to_asn1)
711 authenticator = Authenticator()
712 authenticator['authenticator-vno'] = 5
713 authenticator['crealm'] = domain
714 seq_set(authenticator, 'cname', userName.components_to_asn1)
715 now = datetime.datetime.utcnow()
717 authenticator['cusec'] = now.microsecond
718 authenticator['ctime'] = KerberosTime.to_asn1(now)
720 encodedAuthenticator = encoder.encode(authenticator)
722 # Key Usage 11
723 # AP-REQ Authenticator (includes application authenticator
724 # subkey), encrypted with the application session key
725 # (Section 5.5.1)
726 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
728 apReq['authenticator'] = noValue
729 apReq['authenticator']['etype'] = cipher.enctype
730 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
732 blob['MechToken'] = struct.pack('B', ASN1_AID) + asn1encode( struct.pack('B', ASN1_OID) + asn1encode(
733 TypesMech['KRB5 - Kerberos 5'] ) + KRB5_AP_REQ + encoder.encode(apReq))
735 sessionSetup['SecurityBufferLength'] = len(blob)
736 sessionSetup['Buffer'] = blob.getData()
738 packet = self.SMB_PACKET()
739 packet['Command'] = SMB2_SESSION_SETUP
740 packet['Data'] = sessionSetup
742 #Initiate session preauth hash
743 self._Session['PreauthIntegrityHashValue'] = self._Connection['PreauthIntegrityHashValue']
745 packetID = self.sendSMB(packet)
746 ans = self.recvSMB(packetID)
747 if ans.isValidAnswer(STATUS_SUCCESS): 747 ↛ 848line 747 didn't jump to line 848, because the condition on line 747 was never false
748 self._Session['SessionID'] = ans['SessionID']
749 self._Session['SigningRequired'] = self._Connection['RequireSigning']
750 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
751 self._Session['Connection'] = self._NetBIOSSession.get_socket()
754 if mutualAuth == True: 754 ↛ 756line 754 didn't jump to line 756, because the condition on line 754 was never true
755 #Lets get the session key in the AP_REP
756 from impacket.krb5.asn1 import AP_REP, EncAPRepPart
757 from impacket.krb5.crypto import Key, _enctype_table
758 smbSessSetupResp = SMB2SessionSetup_Response(ans['Data'])
760 #in [KILE] 3.1.1.2:
761 # The subkey in the EncAPRepPart of the KRB_AP_REP message is used as the session key when
762 # MutualAuthentication is requested. (The KRB_AP_REP message and its fields are defined in [RFC4120]
763 # section 5.5.2.) When DES and RC4 are used, the implementation is as described in [RFC1964]. With
764 # DES and RC4, the subkey in the KRB_AP_REQ message can be used as the session key, as it is the
765 # same as the subkey in KRB_AP_REP message; however when AES is used (see [RFC4121]), the
766 # subkeys are different and the subkey in the KRB_AP_REP is used. (The KRB_AP_REQ message is
767 # defined in [RFC4120] section 5.5.1).
768 negTokenResp = SPNEGO_NegTokenResp(smbSessSetupResp['Buffer'])
770 #TODO: Parse ResponseToken as krb5Blob depending on the supported mech indicated in the negTokenResp
771 ap_rep = decoder.decode(negTokenResp['ResponseToken'][16:], asn1Spec=AP_REP())[0]
773 if cipher.enctype != ap_rep['enc-part']['etype']:
774 raise Exception('Unable to decrypt AP_REP: cipher does not match TGS session key')
776 # Key Usage 12
777 # AP-REP encrypted part (includes application session
778 # subkey), encrypted with the application session key
779 # (Section 5.5.2)
780 cipherText = ap_rep['enc-part']['cipher']
781 plainText = cipher.decrypt(sessionKey, 12, cipherText)
783 encAPRepPart = decoder.decode(plainText, asn1Spec = EncAPRepPart())[0]
785 apCipher = _enctype_table[int(encAPRepPart['subkey']['keytype'])]()
786 apSessionKey = Key(apCipher.enctype, encAPRepPart['subkey']['keyvalue'].asOctets())
788 sequenceNumber = int(encAPRepPart['seq-number'])
789 self._Session['SessionKey'] = apSessionKey.contents
791 else:
792 self._Session['SessionKey'] = sessionKey.contents[:16]
794 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] >= SMB2_DIALECT_30:
795 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBSigningKey" as the label;
796 # otherwise, the case - sensitive ASCII string "SMB2AESCMAC" as the label.
797 # If Connection.Dialect is "3.1.1", Session.PreauthIntegrityHashValue as the context; otherwise,
798 # the case-sensitive ASCII string "SmbSign" as context for the algorithm.
799 if self._Connection['Dialect'] == SMB2_DIALECT_311: 799 ↛ 800line 799 didn't jump to line 800, because the condition on line 799 was never true
800 self._Session['SigningKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBSigningKey\x00",
801 self._Session['PreauthIntegrityHashValue'], 128)
802 else:
803 self._Session['SigningKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCMAC\x00",
804 b"SmbSign\x00", 128)
806 # Do not encrypt anonymous connections
807 if user == '' or self.isGuestSession(): 807 ↛ 808line 807 didn't jump to line 808, because the condition on line 807 was never true
808 self._Connection['SupportsEncryption'] = False
810 if self._Session['SigningRequired'] is True: 810 ↛ 812line 810 didn't jump to line 812, because the condition on line 810 was never false
811 self._Session['SigningActivated'] = True
812 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True:
813 # Encryption available. Let's enforce it if we have AES CCM available
814 self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA
815 # Application Key
816 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBAppKey" as the label;
817 # otherwise, the case-sensitive ASCII string "SMB2APP" as the label. Session.PreauthIntegrityHashValue
818 # as the context; otherwise, the case-sensitive ASCII string "SmbRpc" as context for the algorithm.
819 # Encryption Key
820 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBC2SCipherKey" as # the label;
821 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue
822 # as the context; otherwise, the case-sensitive ASCII string "ServerIn " as context for the algorithm
823 # (note the blank space at the end)
824 # Decryption Key
825 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBS2CCipherKey" as the label;
826 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue
827 # as the context; otherwise, the case-sensitive ASCII string "ServerOut" as context for the algorithm.
828 if self._Connection['Dialect'] == SMB2_DIALECT_311: 828 ↛ 829line 828 didn't jump to line 829, because the condition on line 828 was never true
829 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBAppKey\x00",
830 self._Session['PreauthIntegrityHashValue'], 128)
831 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBC2SCipherKey\x00",
832 self._Session['PreauthIntegrityHashValue'], 128)
833 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBS2CCipherKey\x00",
834 self._Session['PreauthIntegrityHashValue'], 128)
835 else:
836 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2APP\x00",
837 b"SmbRpc\x00", 128)
838 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCCM\x00",
839 b"ServerIn \x00", 128)
840 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCCM\x00",
841 b"ServerOut\x00", 128)
843 self._Session['CalculatePreAuthHash'] = False
844 return True
845 else:
846 # We clean the stuff we used in case we want to authenticate again
847 # within the same connection
848 self._Session['UserCredentials'] = ''
849 self._Session['Connection'] = 0
850 self._Session['SessionID'] = 0
851 self._Session['SigningRequired'] = False
852 self._Session['SigningKey'] = ''
853 self._Session['SessionKey'] = ''
854 self._Session['SigningActivated'] = False
855 self._Session['CalculatePreAuthHash'] = False
856 self._Session['PreauthIntegrityHashValue'] = a2b_hex(b'0'*128)
857 raise Exception('Unsuccessful Login')
860 def login(self, user, password, domain = '', lmhash = '', nthash = ''):
861 # If we have hashes, normalize them
862 if lmhash != '' or nthash != '':
863 if len(lmhash) % 2: lmhash = '0%s' % lmhash
864 if len(nthash) % 2: nthash = '0%s' % nthash
865 try: # just in case they were converted already
866 lmhash = a2b_hex(lmhash)
867 nthash = a2b_hex(nthash)
868 except:
869 pass
871 self.__userName = user
872 self.__password = password
873 self.__domain = domain
874 self.__lmhash = lmhash
875 self.__nthash = nthash
876 self.__aesKey = ''
877 self.__TGT = None
878 self.__TGS = None
880 sessionSetup = SMB2SessionSetup()
881 if self.RequireMessageSigning is True: 881 ↛ 882line 881 didn't jump to line 882, because the condition on line 881 was never true
882 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
883 else:
884 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
886 sessionSetup['Flags'] = 0
887 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
889 # Let's build a NegTokenInit with the NTLMSSP
890 # TODO: In the future we should be able to choose different providers
892 blob = SPNEGO_NegTokenInit()
894 # NTLMSSP
895 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
896 auth = ntlm.getNTLMSSPType1(self._Connection['ClientName'],domain, self._Connection['RequireSigning'])
897 blob['MechToken'] = auth.getData()
899 sessionSetup['SecurityBufferLength'] = len(blob)
900 sessionSetup['Buffer'] = blob.getData()
902 # ToDo:
903 # If this authentication is for establishing an alternative channel for an existing Session, as specified
904 # in section 3.2.4.1.7, the client MUST also set the following values:
905 # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new
906 # channel being established.
907 # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field.
908 # The PreviousSessionId field MUST be set to zero.
910 packet = self.SMB_PACKET()
911 packet['Command'] = SMB2_SESSION_SETUP
912 packet['Data'] = sessionSetup
914 packetID = self.sendSMB(packet)
915 ans = self.recvSMB(packetID)
916 if self._Connection['Dialect'] == SMB2_DIALECT_311: 916 ↛ 917line 916 didn't jump to line 917, because the condition on line 916 was never true
917 self.__UpdatePreAuthHash (ans.rawData)
919 if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED): 919 ↛ exitline 919 didn't return from function 'login', because the condition on line 919 was never false
920 self._Session['SessionID'] = ans['SessionID']
921 self._Session['SigningRequired'] = self._Connection['RequireSigning']
922 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
923 self._Session['Connection'] = self._NetBIOSSession.get_socket()
924 sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
925 respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
927 # Let's parse some data and keep it to ourselves in case it is asked
928 ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
929 if ntlmChallenge['TargetInfoFields_len'] > 0: 929 ↛ 971line 929 didn't jump to line 971, because the condition on line 929 was never false
930 av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
931 if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: 931 ↛ 937line 931 didn't jump to line 937, because the condition on line 931 was never false
932 try:
933 self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
934 except:
935 # For some reason, we couldn't decode Unicode here.. silently discard the operation
936 pass
937 if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: 937 ↛ 944line 937 didn't jump to line 944, because the condition on line 937 was never false
938 try:
939 if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): 939 ↛ 944line 939 didn't jump to line 944, because the condition on line 939 was never false
940 self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
941 except:
942 # For some reason, we couldn't decode Unicode here.. silently discard the operation
943 pass
944 if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: 944 ↛ 951line 944 didn't jump to line 951, because the condition on line 944 was never false
945 try:
946 self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
947 except:
948 # For some reason, we couldn't decode Unicode here.. silently discard the operation
949 pass
951 if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] is not None: 951 ↛ 958line 951 didn't jump to line 958, because the condition on line 951 was never false
952 try:
953 self._Session['ServerDNSHostName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode('utf-16le')
954 except:
955 # For some reason, we couldn't decode Unicode here.. silently discard the operation
956 pass
958 if self._strict_hostname_validation: 958 ↛ 959line 958 didn't jump to line 959, because the condition on line 958 was never true
959 self.perform_hostname_validation()
961 # Parse Version to know the target Operating system name. Not provided elsewhere anymore
962 if 'Version' in ntlmChallenge.fields: 962 ↛ 971line 962 didn't jump to line 971, because the condition on line 962 was never false
963 version = ntlmChallenge['Version']
965 if len(version) >= 4: 965 ↛ 971line 965 didn't jump to line 971, because the condition on line 965 was never false
966 self._Session['ServerOS'] = "Windows %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack('<H',version[2:4])[0])
967 self._Session["ServerOSMajor"] = indexbytes(version,0)
968 self._Session["ServerOSMinor"] = indexbytes(version,1)
969 self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0]
971 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash)
975 respToken2 = SPNEGO_NegTokenResp()
976 respToken2['ResponseToken'] = type3.getData()
978 # Reusing the previous structure
979 sessionSetup['SecurityBufferLength'] = len(respToken2)
980 sessionSetup['Buffer'] = respToken2.getData()
982 packetID = self.sendSMB(packet)
983 packet = self.recvSMB(packetID)
985 # Let's calculate Key Materials before moving on
986 if exportedSessionKey is not None: 986 ↛ 1001line 986 didn't jump to line 1001, because the condition on line 986 was never false
987 self._Session['SessionKey'] = exportedSessionKey
988 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] >= SMB2_DIALECT_30:
989 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBSigningKey" as the label;
990 # otherwise, the case - sensitive ASCII string "SMB2AESCMAC" as the label.
991 # If Connection.Dialect is "3.1.1", Session.PreauthIntegrityHashValue as the context; otherwise,
992 # the case-sensitive ASCII string "SmbSign" as context for the algorithm.
993 if self._Connection['Dialect'] == SMB2_DIALECT_311: 993 ↛ 994line 993 didn't jump to line 994, because the condition on line 993 was never true
994 self._Session['SigningKey'] = crypto.KDF_CounterMode (exportedSessionKey,
995 b"SMBSigningKey\x00",
996 self._Session['PreauthIntegrityHashValue'],
997 128)
998 else:
999 self._Session['SigningKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCMAC\x00",
1000 b"SmbSign\x00", 128)
1001 try:
1002 if packet.isValidAnswer(STATUS_SUCCESS): 1002 ↛ exitline 1002 didn't return from function 'login', because the condition on line 1002 was never false
1003 sessionSetupResponse = SMB2SessionSetup_Response(packet['Data'])
1004 self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags']
1005 self._Session['SessionID'] = packet['SessionID']
1007 # Do not encrypt anonymous connections
1008 if user == '' or self.isGuestSession():
1009 self._Connection['SupportsEncryption'] = False
1011 # Calculate the key derivations for dialect 3.0
1012 if self._Session['SigningRequired'] is True: 1012 ↛ 1014line 1012 didn't jump to line 1014, because the condition on line 1012 was never false
1013 self._Session['SigningActivated'] = True
1014 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True:
1015 # SMB 3.0. Encryption available. Let's enforce it if we have AES CCM available
1016 self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA
1017 # Application Key
1018 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBAppKey" as the label;
1019 # otherwise, the case-sensitive ASCII string "SMB2APP" as the label. Session.PreauthIntegrityHashValue
1020 # as the context; otherwise, the case-sensitive ASCII string "SmbRpc" as context for the algorithm.
1021 # Encryption Key
1022 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBC2SCipherKey" as # the label;
1023 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue
1024 # as the context; otherwise, the case-sensitive ASCII string "ServerIn " as context for the algorithm
1025 # (note the blank space at the end)
1026 # Decryption Key
1027 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBS2CCipherKey" as the label;
1028 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue
1029 # as the context; otherwise, the case-sensitive ASCII string "ServerOut" as context for the algorithm.
1030 if self._Connection['Dialect'] == SMB2_DIALECT_311: 1030 ↛ 1031line 1030 didn't jump to line 1031, because the condition on line 1030 was never true
1031 self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMBAppKey\x00",
1032 self._Session['PreauthIntegrityHashValue'], 128)
1033 self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMBC2SCipherKey\x00",
1034 self._Session['PreauthIntegrityHashValue'], 128)
1035 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMBS2CCipherKey\x00",
1036 self._Session['PreauthIntegrityHashValue'], 128)
1038 else:
1039 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2APP\x00",
1040 b"SmbRpc\x00", 128)
1041 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCCM\x00",
1042 b"ServerIn \x00", 128)
1043 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCCM\x00",
1044 b"ServerOut\x00", 128)
1045 self._Session['CalculatePreAuthHash'] = False
1046 return True
1047 except:
1048 # We clean the stuff we used in case we want to authenticate again
1049 # within the same connection
1050 self._Session['UserCredentials'] = ''
1051 self._Session['Connection'] = 0
1052 self._Session['SessionID'] = 0
1053 self._Session['SigningRequired'] = False
1054 self._Session['SigningKey'] = ''
1055 self._Session['SessionKey'] = ''
1056 self._Session['SigningActivated'] = False
1057 self._Session['CalculatePreAuthHash'] = False
1058 self._Session['PreauthIntegrityHashValue'] = a2b_hex(b'0'*128)
1059 raise
1061 def connectTree(self, share):
1063 # Just in case this came with the full path (maybe an SMB1 client), let's just leave
1064 # the sharename, we'll take care of the rest
1066 #print self._Session['TreeConnectTable']
1067 share = share.split('\\')[-1]
1068 if share in self._Session['TreeConnectTable']:
1069 # Already connected, no need to reconnect
1070 treeEntry = self._Session['TreeConnectTable'][share]
1071 treeEntry['NumberOfUses'] += 1
1072 self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1
1073 return treeEntry['TreeConnectId']
1075 #path = share
1076 try:
1077 _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0]
1078 remoteHost = sockaddr[0]
1079 except:
1080 remoteHost = self._Connection['ServerIP']
1081 path = '\\\\' + remoteHost + '\\' +share
1083 treeConnect = SMB2TreeConnect()
1084 treeConnect['Buffer'] = path.encode('utf-16le')
1085 treeConnect['PathLength'] = len(path)*2
1087 packet = self.SMB_PACKET()
1088 packet['Command'] = SMB2_TREE_CONNECT
1089 packet['Data'] = treeConnect
1090 packetID = self.sendSMB(packet)
1091 packet = self.recvSMB(packetID)
1092 if packet.isValidAnswer(STATUS_SUCCESS): 1092 ↛ exitline 1092 didn't return from function 'connectTree', because the condition on line 1092 was never false
1093 treeConnectResponse = SMB2TreeConnect_Response(packet['Data'])
1094 treeEntry = copy.deepcopy(TREE_CONNECT)
1095 treeEntry['ShareName'] = share
1096 treeEntry['TreeConnectId'] = packet['TreeID']
1097 treeEntry['Session'] = packet['SessionID']
1098 treeEntry['NumberOfUses'] += 1
1099 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS: 1099 ↛ 1100line 1099 didn't jump to line 1100, because the condition on line 1099 was never true
1100 treeEntry['IsDfsShare'] = True
1101 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY: 1101 ↛ 1102line 1101 didn't jump to line 1102, because the condition on line 1101 was never true
1102 treeEntry['IsCAShare'] = True
1104 if self._Connection['Dialect'] >= SMB2_DIALECT_30:
1105 if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA): 1105 ↛ 1106line 1105 didn't jump to line 1106, because the condition on line 1105 was never true
1106 treeEntry['EncryptData'] = True
1107 # ToDo: This and what follows
1108 # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a
1109 # decryption key as specified in section 3.1.4.2, by providing the following inputs and store
1110 # them in Session.EncryptionKey and Session.DecryptionKey:
1111 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT: 1111 ↛ 1112line 1111 didn't jump to line 1112, because the condition on line 1111 was never true
1112 treeEntry['IsScaleoutShare'] = True
1114 self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry
1115 self._Session['TreeConnectTable'][share] = treeEntry
1117 return packet['TreeID']
1119 def disconnectTree(self, treeId):
1120 if (treeId in self._Session['TreeConnectTable']) is False: 1120 ↛ 1121line 1120 didn't jump to line 1121, because the condition on line 1120 was never true
1121 raise SessionError(STATUS_INVALID_PARAMETER)
1123 if treeId in self._Session['TreeConnectTable']: 1123 ↛ 1131line 1123 didn't jump to line 1131, because the condition on line 1123 was never false
1124 # More than 1 use? descrease it and return, if not, send the packet
1125 if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
1126 treeEntry = self._Session['TreeConnectTable'][treeId]
1127 treeEntry['NumberOfUses'] -= 1
1128 self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1
1129 return True
1131 packet = self.SMB_PACKET()
1132 packet['Command'] = SMB2_TREE_DISCONNECT
1133 packet['TreeID'] = treeId
1134 treeDisconnect = SMB2TreeDisconnect()
1135 packet['Data'] = treeDisconnect
1136 packetID = self.sendSMB(packet)
1137 packet = self.recvSMB(packetID)
1138 if packet.isValidAnswer(STATUS_SUCCESS): 1138 ↛ exitline 1138 didn't return from function 'disconnectTree', because the condition on line 1138 was never false
1139 shareName = self._Session['TreeConnectTable'][treeId]['ShareName']
1140 del(self._Session['TreeConnectTable'][shareName])
1141 del(self._Session['TreeConnectTable'][treeId])
1142 filesIDToBeRemoved = []
1143 for fileID in list(self._Session['OpenTable'].keys()):
1144 if self._Session['OpenTable'][fileID]['TreeConnect'] == treeId:
1145 filesIDToBeRemoved.append(fileID)
1146 for fileIDToBeRemoved in filesIDToBeRemoved:
1147 del(self._Session['OpenTable'][fileIDToBeRemoved])
1148 return True
1150 def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
1151 if (treeId in self._Session['TreeConnectTable']) is False: 1151 ↛ 1152line 1151 didn't jump to line 1152, because the condition on line 1151 was never true
1152 raise SessionError(STATUS_INVALID_PARAMETER)
1154 fileName = fileName.replace('/', '\\')
1155 if len(fileName) > 0:
1156 fileName = ntpath.normpath(fileName)
1157 if fileName[0] == '\\':
1158 fileName = fileName[1:]
1160 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 1160 ↛ 1161line 1160 didn't jump to line 1161, because the condition on line 1160 was never true
1161 pathName = fileName
1162 else:
1163 pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName
1165 fileEntry = copy.deepcopy(FILE)
1166 fileEntry['LeaseKey'] = uuid.generate()
1167 fileEntry['LeaseState'] = SMB2_LEASE_NONE
1168 self.GlobalFileTable[pathName] = fileEntry
1170 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True: 1170 ↛ 1172line 1170 didn't jump to line 1172, because the condition on line 1170 was never true
1171 # Is this file NOT on the root directory?
1172 if len(fileName.split('\\')) > 2:
1173 parentDir = ntpath.dirname(pathName)
1174 if parentDir in self.GlobalFileTable:
1175 raise Exception("Don't know what to do now! :-o")
1176 else:
1177 parentEntry = copy.deepcopy(FILE)
1178 parentEntry['LeaseKey'] = uuid.generate()
1179 parentEntry['LeaseState'] = SMB2_LEASE_NONE
1180 self.GlobalFileTable[parentDir] = parentEntry
1182 packet = self.SMB_PACKET()
1183 packet['Command'] = SMB2_CREATE
1184 packet['TreeID'] = treeId
1185 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 1185 ↛ 1186line 1185 didn't jump to line 1186, because the condition on line 1185 was never true
1186 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS
1188 smb2Create = SMB2Create()
1189 smb2Create['SecurityFlags'] = 0
1190 smb2Create['RequestedOplockLevel'] = oplockLevel
1191 smb2Create['ImpersonationLevel'] = impersonationLevel
1192 smb2Create['DesiredAccess'] = desiredAccess
1193 smb2Create['FileAttributes'] = fileAttributes
1194 smb2Create['ShareAccess'] = shareMode
1195 smb2Create['CreateDisposition'] = creationDisposition
1196 smb2Create['CreateOptions'] = creationOptions
1198 smb2Create['NameLength'] = len(fileName)*2
1199 if fileName != '':
1200 smb2Create['Buffer'] = fileName.encode('utf-16le')
1201 else:
1202 smb2Create['Buffer'] = b'\x00'
1204 if createContexts is not None: 1204 ↛ 1205line 1204 didn't jump to line 1205, because the condition on line 1204 was never true
1205 contextsBuf = b''.join(x.getData() for x in createContexts)
1206 smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + len(smb2Create['Buffer'])
1208 # pad offset to 8-byte align
1209 if (smb2Create['CreateContextsOffset'] % 8):
1210 smb2Create['Buffer'] += b'\x00'*(8-(smb2Create['CreateContextsOffset'] % 8))
1211 smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + len(smb2Create['Buffer'])
1213 smb2Create['CreateContextsLength'] = len(contextsBuf)
1214 smb2Create['Buffer'] += contextsBuf
1215 else:
1216 smb2Create['CreateContextsOffset'] = 0
1217 smb2Create['CreateContextsLength'] = 0
1219 packet['Data'] = smb2Create
1221 packetID = self.sendSMB(packet)
1222 ans = self.recvSMB(packetID)
1223 if ans.isValidAnswer(STATUS_SUCCESS):
1224 createResponse = SMB2Create_Response(ans['Data'])
1226 openFile = copy.deepcopy(OPEN)
1227 openFile['FileID'] = createResponse['FileID']
1228 openFile['TreeConnect'] = treeId
1229 openFile['Oplocklevel'] = oplockLevel
1230 openFile['Durable'] = False
1231 openFile['ResilientHandle'] = False
1232 openFile['LastDisconnectTime'] = 0
1233 openFile['FileName'] = pathName
1235 # ToDo: Complete the OperationBuckets
1236 if self._Connection['Dialect'] >= SMB2_DIALECT_30:
1237 openFile['DesiredAccess'] = oplockLevel
1238 openFile['ShareMode'] = oplockLevel
1239 openFile['CreateOptions'] = oplockLevel
1240 openFile['FileAttributes'] = oplockLevel
1241 openFile['CreateDisposition'] = oplockLevel
1243 # ToDo: Process the contexts
1244 self._Session['OpenTable'][createResponse['FileID'].getData()] = openFile
1246 # The client MUST generate a handle for the Open, and it MUST
1247 # return success and the generated handle to the calling application.
1248 # In our case, str(FileID)
1249 return createResponse['FileID'].getData()
1251 def close(self, treeId, fileId):
1252 if (treeId in self._Session['TreeConnectTable']) is False: 1252 ↛ 1253line 1252 didn't jump to line 1253, because the condition on line 1252 was never true
1253 raise SessionError(STATUS_INVALID_PARAMETER)
1254 if (fileId in self._Session['OpenTable']) is False: 1254 ↛ 1255line 1254 didn't jump to line 1255, because the condition on line 1254 was never true
1255 raise SessionError(STATUS_INVALID_PARAMETER)
1257 packet = self.SMB_PACKET()
1258 packet['Command'] = SMB2_CLOSE
1259 packet['TreeID'] = treeId
1261 smbClose = SMB2Close()
1262 smbClose['Flags'] = 0
1263 smbClose['FileID'] = fileId
1265 packet['Data'] = smbClose
1267 packetID = self.sendSMB(packet)
1268 ans = self.recvSMB(packetID)
1270 if ans.isValidAnswer(STATUS_SUCCESS): 1270 ↛ exitline 1270 didn't return from function 'close', because the condition on line 1270 was never false
1271 del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']])
1272 del(self._Session['OpenTable'][fileId])
1274 # ToDo Remove stuff from GlobalFileTable
1275 return True
1277 def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True):
1278 # IMPORTANT NOTE: As you can see, this was coded as a recursive function
1279 # Hence, you can exhaust the memory pretty easy ( large bytesToRead )
1280 # This function should NOT be used for reading files directly, but another higher
1281 # level function should be used that will break the read into smaller pieces
1283 if (treeId in self._Session['TreeConnectTable']) is False: 1283 ↛ 1284line 1283 didn't jump to line 1284, because the condition on line 1283 was never true
1284 raise SessionError(STATUS_INVALID_PARAMETER)
1285 if (fileId in self._Session['OpenTable']) is False: 1285 ↛ 1286line 1285 didn't jump to line 1286, because the condition on line 1285 was never true
1286 raise SessionError(STATUS_INVALID_PARAMETER)
1288 packet = self.SMB_PACKET()
1289 packet['Command'] = SMB2_READ
1290 packet['TreeID'] = treeId
1292 if self._Connection['MaxReadSize'] < bytesToRead: 1292 ↛ 1293line 1292 didn't jump to line 1293, because the condition on line 1292 was never true
1293 maxBytesToRead = self._Connection['MaxReadSize']
1294 else:
1295 maxBytesToRead = bytesToRead
1297 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
1298 packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) // 65536)
1299 else:
1300 maxBytesToRead = min(65536,bytesToRead)
1302 smbRead = SMB2Read()
1303 smbRead['Padding'] = 0x50
1304 smbRead['FileID'] = fileId
1305 smbRead['Length'] = maxBytesToRead
1306 smbRead['Offset'] = offset
1307 packet['Data'] = smbRead
1309 packetID = self.sendSMB(packet)
1310 ans = self.recvSMB(packetID)
1312 if ans.isValidAnswer(STATUS_SUCCESS): 1312 ↛ exitline 1312 didn't return from function 'read', because the condition on line 1312 was never false
1313 readResponse = SMB2Read_Response(ans['Data'])
1314 retData = readResponse['Buffer']
1315 if readResponse['DataRemaining'] > 0: 1315 ↛ 1316line 1315 didn't jump to line 1316, because the condition on line 1315 was never true
1316 retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer)
1317 return retData
1319 def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True):
1320 # IMPORTANT NOTE: As you can see, this was coded as a recursive function
1321 # Hence, you can exhaust the memory pretty easy ( large bytesToWrite )
1322 # This function should NOT be used for writing directly to files, but another higher
1323 # level function should be used that will break the writes into smaller pieces
1325 if (treeId in self._Session['TreeConnectTable']) is False: 1325 ↛ 1326line 1325 didn't jump to line 1326, because the condition on line 1325 was never true
1326 raise SessionError(STATUS_INVALID_PARAMETER)
1327 if (fileId in self._Session['OpenTable']) is False: 1327 ↛ 1328line 1327 didn't jump to line 1328, because the condition on line 1327 was never true
1328 raise SessionError(STATUS_INVALID_PARAMETER)
1330 packet = self.SMB_PACKET()
1331 packet['Command'] = SMB2_WRITE
1332 packet['TreeID'] = treeId
1334 if self._Connection['MaxWriteSize'] < bytesToWrite: 1334 ↛ 1335line 1334 didn't jump to line 1335, because the condition on line 1334 was never true
1335 maxBytesToWrite = self._Connection['MaxWriteSize']
1336 else:
1337 maxBytesToWrite = bytesToWrite
1339 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
1340 packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) // 65536)
1341 else:
1342 maxBytesToWrite = min(65536,bytesToWrite)
1344 smbWrite = SMB2Write()
1345 smbWrite['FileID'] = fileId
1346 smbWrite['Length'] = maxBytesToWrite
1347 smbWrite['Offset'] = offset
1348 smbWrite['WriteChannelInfoOffset'] = 0
1349 smbWrite['Buffer'] = data[:maxBytesToWrite]
1350 packet['Data'] = smbWrite
1352 packetID = self.sendSMB(packet)
1353 if waitAnswer is True: 1353 ↛ 1356line 1353 didn't jump to line 1356, because the condition on line 1353 was never false
1354 ans = self.recvSMB(packetID)
1355 else:
1356 return maxBytesToWrite
1358 if ans.isValidAnswer(STATUS_SUCCESS):
1359 writeResponse = SMB2Write_Response(ans['Data'])
1360 bytesWritten = writeResponse['Count']
1361 if bytesWritten < bytesToWrite: 1361 ↛ 1362line 1361 didn't jump to line 1362, because the condition on line 1361 was never true
1362 bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer)
1363 return bytesWritten
1365 def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
1366 if (treeId in self._Session['TreeConnectTable']) is False: 1366 ↛ 1367line 1366 didn't jump to line 1367, because the condition on line 1366 was never true
1367 raise SessionError(STATUS_INVALID_PARAMETER)
1368 if (fileId in self._Session['OpenTable']) is False: 1368 ↛ 1369line 1368 didn't jump to line 1369, because the condition on line 1368 was never true
1369 raise SessionError(STATUS_INVALID_PARAMETER)
1371 packet = self.SMB_PACKET()
1372 packet['Command'] = SMB2_QUERY_DIRECTORY
1373 packet['TreeID'] = treeId
1375 queryDirectory = SMB2QueryDirectory()
1376 queryDirectory['FileInformationClass'] = informationClass
1377 if resumeIndex != 0 : 1377 ↛ 1378line 1377 didn't jump to line 1378, because the condition on line 1377 was never true
1378 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED
1379 queryDirectory['FileIndex'] = resumeIndex
1380 queryDirectory['FileID'] = fileId
1381 if maxBufferSize is None: 1381 ↛ 1382line 1381 didn't jump to line 1382, because the condition on line 1381 was never true
1382 maxBufferSize = self._Connection['MaxReadSize']
1383 queryDirectory['OutputBufferLength'] = maxBufferSize
1384 queryDirectory['FileNameLength'] = len(searchString)*2
1385 queryDirectory['Buffer'] = searchString.encode('utf-16le')
1387 packet['Data'] = queryDirectory
1389 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
1390 packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) // 65536)
1392 packetID = self.sendSMB(packet)
1393 ans = self.recvSMB(packetID)
1394 if ans.isValidAnswer(STATUS_SUCCESS):
1395 queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data'])
1396 return queryDirectoryResponse['Buffer']
1398 def echo(self):
1399 packet = self.SMB_PACKET()
1400 packet['Command'] = SMB2_ECHO
1401 smbEcho = SMB2Echo()
1402 packet['Data'] = smbEcho
1403 packetID = self.sendSMB(packet)
1404 ans = self.recvSMB(packetID)
1405 if ans.isValidAnswer(STATUS_SUCCESS):
1406 return True
1408 def cancel(self, packetID):
1409 packet = self.SMB_PACKET()
1410 packet['Command'] = SMB2_CANCEL
1411 packet['MessageID'] = packetID
1413 smbCancel = SMB2Cancel()
1415 packet['Data'] = smbCancel
1416 self.sendSMB(packet)
1418 def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
1419 if (treeId in self._Session['TreeConnectTable']) is False:
1420 raise SessionError(STATUS_INVALID_PARAMETER)
1421 if fileId is None:
1422 fileId = '\xff'*16
1423 else:
1424 if (fileId in self._Session['OpenTable']) is False:
1425 raise SessionError(STATUS_INVALID_PARAMETER)
1427 packet = self.SMB_PACKET()
1428 packet['Command'] = SMB2_IOCTL
1429 packet['TreeID'] = treeId
1431 smbIoctl = SMB2Ioctl()
1432 smbIoctl['FileID'] = fileId
1433 smbIoctl['CtlCode'] = ctlCode
1434 smbIoctl['MaxInputResponse'] = maxInputResponse
1435 smbIoctl['MaxOutputResponse'] = maxOutputResponse
1436 smbIoctl['InputCount'] = len(inputBlob)
1437 if len(inputBlob) == 0:
1438 smbIoctl['InputOffset'] = 0
1439 smbIoctl['Buffer'] = '\x00'
1440 else:
1441 smbIoctl['Buffer'] = inputBlob
1442 smbIoctl['OutputOffset'] = 0
1443 smbIoctl['MaxOutputResponse'] = maxOutputResponse
1444 smbIoctl['Flags'] = flags
1446 packet['Data'] = smbIoctl
1448 packetID = self.sendSMB(packet)
1450 if waitAnswer == 0:
1451 return True
1453 ans = self.recvSMB(packetID)
1455 if ans.isValidAnswer(STATUS_SUCCESS):
1456 smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
1457 return smbIoctlResponse['Buffer']
1459 def flush(self,treeId, fileId):
1460 if (treeId in self._Session['TreeConnectTable']) is False:
1461 raise SessionError(STATUS_INVALID_PARAMETER)
1462 if (fileId in self._Session['OpenTable']) is False:
1463 raise SessionError(STATUS_INVALID_PARAMETER)
1465 packet = self.SMB_PACKET()
1466 packet['Command'] = SMB2_FLUSH
1467 packet['TreeID'] = treeId
1469 smbFlush = SMB2Flush()
1470 smbFlush['FileID'] = fileId
1472 packet['Data'] = smbFlush
1474 packetID = self.sendSMB(packet)
1475 ans = self.recvSMB(packetID)
1477 if ans.isValidAnswer(STATUS_SUCCESS):
1478 return True
1480 def lock(self, treeId, fileId, locks, lockSequence = 0):
1481 if (treeId in self._Session['TreeConnectTable']) is False:
1482 raise SessionError(STATUS_INVALID_PARAMETER)
1483 if (fileId in self._Session['OpenTable']) is False:
1484 raise SessionError(STATUS_INVALID_PARAMETER)
1486 packet = self.SMB_PACKET()
1487 packet['Command'] = SMB2_LOCK
1488 packet['TreeID'] = treeId
1490 smbLock = SMB2Lock()
1491 smbLock['FileID'] = fileId
1492 smbLock['LockCount'] = len(locks)
1493 smbLock['LockSequence'] = lockSequence
1494 smbLock['Locks'] = ''.join(str(x) for x in locks)
1496 packet['Data'] = smbLock
1498 packetID = self.sendSMB(packet)
1499 ans = self.recvSMB(packetID)
1501 if ans.isValidAnswer(STATUS_SUCCESS):
1502 smbFlushResponse = SMB2Lock_Response(ans['Data'])
1503 return True
1505 # ToDo:
1506 # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST
1507 # do the following:
1508 # The client MUST scan through Open.OperationBuckets and find an element with its Free field
1509 # set to TRUE. If no such element could be found, an implementation-specific error MUST be
1510 # returned to the application.
1511 # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and
1512 # let BucketNumber = BucketIndex +1.
1513 # Set Open.OperationBuckets[BucketIndex].Free = FALSE
1514 # Let the SequenceNumber of the element chosen above be referred to as BucketSequence.
1515 # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) +
1516 # BucketSequence.
1517 # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic.
1519 def logoff(self):
1520 packet = self.SMB_PACKET()
1521 packet['Command'] = SMB2_LOGOFF
1523 smbLogoff = SMB2Logoff()
1525 packet['Data'] = smbLogoff
1527 packetID = self.sendSMB(packet)
1528 ans = self.recvSMB(packetID)
1530 if ans.isValidAnswer(STATUS_SUCCESS): 1530 ↛ exitline 1530 didn't return from function 'logoff', because the condition on line 1530 was never false
1531 # We clean the stuff we used in case we want to authenticate again
1532 # within the same connection
1533 self._Session['UserCredentials'] = ''
1534 self._Session['Connection'] = 0
1535 self._Session['SessionID'] = 0
1536 self._Session['SigningRequired'] = False
1537 self._Session['SigningKey'] = ''
1538 self._Session['SessionKey'] = ''
1539 self._Session['SigningActivated'] = False
1540 return True
1542 def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
1543 if (treeId in self._Session['TreeConnectTable']) is False: 1543 ↛ 1544line 1543 didn't jump to line 1544, because the condition on line 1543 was never true
1544 raise SessionError(STATUS_INVALID_PARAMETER)
1545 if (fileId in self._Session['OpenTable']) is False: 1545 ↛ 1546line 1545 didn't jump to line 1546, because the condition on line 1545 was never true
1546 raise SessionError(STATUS_INVALID_PARAMETER)
1548 packet = self.SMB_PACKET()
1549 packet['Command'] = SMB2_QUERY_INFO
1550 packet['TreeID'] = treeId
1552 queryInfo = SMB2QueryInfo()
1553 queryInfo['FileID'] = fileId
1554 queryInfo['InfoType'] = infoType
1555 queryInfo['FileInfoClass'] = fileInfoClass
1556 queryInfo['OutputBufferLength'] = 65535
1557 queryInfo['AdditionalInformation'] = additionalInformation
1558 if len(inputBlob) == 0: 1558 ↛ 1562line 1558 didn't jump to line 1562, because the condition on line 1558 was never false
1559 queryInfo['InputBufferOffset'] = 0
1560 queryInfo['Buffer'] = '\x00'
1561 else:
1562 queryInfo['InputBufferLength'] = len(inputBlob)
1563 queryInfo['Buffer'] = inputBlob
1564 queryInfo['Flags'] = flags
1566 packet['Data'] = queryInfo
1567 packetID = self.sendSMB(packet)
1568 ans = self.recvSMB(packetID)
1570 if ans.isValidAnswer(STATUS_SUCCESS): 1570 ↛ exitline 1570 didn't return from function 'queryInfo', because the condition on line 1570 was never false
1571 queryResponse = SMB2QueryInfo_Response(ans['Data'])
1572 return queryResponse['Buffer']
1574 def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
1575 if (treeId in self._Session['TreeConnectTable']) is False: 1575 ↛ 1576line 1575 didn't jump to line 1576, because the condition on line 1575 was never true
1576 raise SessionError(STATUS_INVALID_PARAMETER)
1577 if (fileId in self._Session['OpenTable']) is False: 1577 ↛ 1578line 1577 didn't jump to line 1578, because the condition on line 1577 was never true
1578 raise SessionError(STATUS_INVALID_PARAMETER)
1580 packet = self.SMB_PACKET()
1581 packet['Command'] = SMB2_SET_INFO
1582 packet['TreeID'] = treeId
1584 setInfo = SMB2SetInfo()
1585 setInfo['InfoType'] = infoType
1586 setInfo['FileInfoClass'] = fileInfoClass
1587 setInfo['BufferLength'] = len(inputBlob)
1588 setInfo['AdditionalInformation'] = additionalInformation
1589 setInfo['FileID'] = fileId
1590 setInfo['Buffer'] = inputBlob
1592 packet['Data'] = setInfo
1593 packetID = self.sendSMB(packet)
1594 ans = self.recvSMB(packetID)
1596 if ans.isValidAnswer(STATUS_SUCCESS):
1597 return True
1599 def getSessionKey(self):
1600 if self.getDialect() >= SMB2_DIALECT_30:
1601 return self._Session['ApplicationKey']
1602 else:
1603 return self._Session['SessionKey']
1605 def setSessionKey(self, key):
1606 if self.getDialect() >= SMB2_DIALECT_30:
1607 self._Session['ApplicationKey'] = key
1608 else:
1609 self._Session['SessionKey'] = key
1611 ######################################################################
1612 # Higher level functions
1614 def rename(self, shareName, oldPath, newPath):
1615 oldPath = oldPath.replace('/', '\\')
1616 oldPath = ntpath.normpath(oldPath)
1617 if len(oldPath) > 0 and oldPath[0] == '\\': 1617 ↛ 1620line 1617 didn't jump to line 1620, because the condition on line 1617 was never false
1618 oldPath = oldPath[1:]
1620 newPath = newPath.replace('/', '\\')
1621 newPath = ntpath.normpath(newPath)
1622 if len(newPath) > 0 and newPath[0] == '\\': 1622 ↛ 1625line 1622 didn't jump to line 1625, because the condition on line 1622 was never false
1623 newPath = newPath[1:]
1625 treeId = self.connectTree(shareName)
1626 fileId = None
1627 try:
1628 fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0)
1629 renameReq = FILE_RENAME_INFORMATION_TYPE_2()
1630 renameReq['ReplaceIfExists'] = 1
1631 renameReq['RootDirectory'] = '\x00'*8
1632 renameReq['FileNameLength'] = len(newPath)*2
1633 renameReq['FileName'] = newPath.encode('utf-16le')
1634 self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO)
1635 finally:
1636 if fileId is not None: 1636 ↛ 1638line 1636 didn't jump to line 1638, because the condition on line 1636 was never false
1637 self.close(treeId, fileId)
1638 self.disconnectTree(treeId)
1640 return True
1642 def writeFile(self, treeId, fileId, data, offset = 0):
1643 finished = False
1644 writeOffset = offset
1645 while not finished: 1645 ↛ 1652line 1645 didn't jump to line 1652, because the condition on line 1645 was never false
1646 if len(data) == 0:
1647 break
1648 writeData = data[:self._Connection['MaxWriteSize']]
1649 data = data[self._Connection['MaxWriteSize']:]
1650 written = self.write(treeId, fileId, writeData, writeOffset, len(writeData))
1651 writeOffset += written
1652 return writeOffset - offset
1654 def isSnapshotRequest(self, path):
1655 #TODO: use a regex here?
1656 return '@GMT-' in path
1658 def timestampForSnapshot(self, path):
1659 timestamp = path[path.index("@GMT-"):path.index("@GMT-")+24]
1660 path = path.replace(timestamp, '')
1661 from datetime import datetime
1662 fTime = int((datetime.strptime(timestamp, '@GMT-%Y.%m.%d-%H.%M.%S') - datetime(1970,1,1)).total_seconds())
1663 fTime *= 10000000
1664 fTime += 116444736000000000
1666 token = SMB2_CREATE_TIMEWARP_TOKEN()
1667 token['Timestamp'] = fTime
1669 ctx = SMB2CreateContext()
1670 ctx['Next'] = 0
1671 ctx['NameOffset'] = 16
1672 ctx['NameLength'] = len('TWrp')
1673 ctx['DataOffset'] = 24
1674 ctx['DataLength'] = 8
1675 ctx['Buffer'] = b'TWrp'
1676 ctx['Buffer'] += b'\x00'*4 # 4 bytes to 8-byte align
1677 ctx['Buffer'] += token.getData()
1679 # fix-up the path
1680 path = path.replace(timestamp, '').replace('\\\\', '\\')
1681 if path == '\\':
1682 path += '*'
1683 return path, ctx
1685 def listPath(self, shareName, path, password = None):
1686 createContexts = None
1688 if self.isSnapshotRequest(path): 1688 ↛ 1689line 1688 didn't jump to line 1689, because the condition on line 1688 was never true
1689 createContexts = []
1690 path, ctx = self.timestampForSnapshot(path)
1691 createContexts.append(ctx)
1693 # ToDo: Handle situations where share is password protected
1694 path = path.replace('/', '\\')
1695 path = ntpath.normpath(path)
1696 if len(path) > 0 and path[0] == '\\': 1696 ↛ 1697line 1696 didn't jump to line 1697, because the condition on line 1696 was never true
1697 path = path[1:]
1699 treeId = self.connectTree(shareName)
1701 fileId = None
1702 try:
1703 # ToDo, we're assuming it's a directory, we should check what the file type is
1704 fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_SHARE_READ |
1705 FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1706 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0,
1707 createContexts=createContexts)
1708 res = ''
1709 files = []
1710 from impacket import smb
1711 while True:
1712 try:
1713 res = self.queryDirectory(treeId, fileId, ntpath.basename(path), maxBufferSize=65535,
1714 informationClass=FILE_FULL_DIRECTORY_INFORMATION)
1715 nextOffset = 1
1716 while nextOffset != 0:
1717 fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE)
1718 fileInfo.fromString(res)
1719 files.append(smb.SharedFile(fileInfo['CreationTime'], fileInfo['LastAccessTime'],
1720 fileInfo['LastChangeTime'], fileInfo['EndOfFile'],
1721 fileInfo['AllocationSize'], fileInfo['ExtFileAttributes'],
1722 fileInfo['FileName'].decode('utf-16le'),
1723 fileInfo['FileName'].decode('utf-16le')))
1724 nextOffset = fileInfo['NextEntryOffset']
1725 res = res[nextOffset:]
1726 except SessionError as e: 1726 ↛ 1730line 1726 didn't jump to line 1730
1727 if (e.get_error_code()) != STATUS_NO_MORE_FILES: 1727 ↛ 1728line 1727 didn't jump to line 1728, because the condition on line 1727 was never true
1728 raise
1729 break
1730 except Exception as e:
1731 print(str(e))
1732 raise
1733 finally:
1734 if fileId is not None: 1734 ↛ 1736line 1734 didn't jump to line 1736, because the condition on line 1734 was never false
1735 self.close(treeId, fileId)
1736 self.disconnectTree(treeId) 1736 ↛ exitline 1736 didn't except from function 'listPath', because the raise on line 1728 wasn't executed or the raise on line 1732 wasn't executed
1738 return files
1740 def mkdir(self, shareName, pathName, password = None):
1741 # ToDo: Handle situations where share is password protected
1742 pathName = pathName.replace('/', '\\')
1743 pathName = ntpath.normpath(pathName)
1744 if len(pathName) > 0 and pathName[0] == '\\': 1744 ↛ 1747line 1744 didn't jump to line 1747, because the condition on line 1744 was never false
1745 pathName = pathName[1:]
1747 treeId = self.connectTree(shareName)
1749 fileId = None
1750 try:
1751 fileId = self.create(treeId, pathName, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1752 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)
1753 finally:
1754 if fileId is not None: 1754 ↛ 1756line 1754 didn't jump to line 1756, because the condition on line 1754 was never false
1755 self.close(treeId, fileId)
1756 self.disconnectTree(treeId)
1758 return True
1760 def rmdir(self, shareName, pathName, password = None):
1761 # ToDo: Handle situations where share is password protected
1762 pathName = pathName.replace('/', '\\')
1763 pathName = ntpath.normpath(pathName)
1764 if len(pathName) > 0 and pathName[0] == '\\': 1764 ↛ 1767line 1764 didn't jump to line 1767, because the condition on line 1764 was never false
1765 pathName = pathName[1:]
1767 treeId = self.connectTree(shareName)
1769 fileId = None
1770 try:
1771 fileId = self.create(treeId, pathName, desiredAccess=DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1772 shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1773 creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT,
1774 creationDisposition=FILE_OPEN, fileAttributes=0)
1775 from impacket import smb
1776 delete_req = smb.SMBSetFileDispositionInfo()
1777 delete_req['DeletePending'] = True
1778 self.setInfo(treeId, fileId, inputBlob=delete_req, fileInfoClass=SMB2_FILE_DISPOSITION_INFO)
1779 finally:
1780 if fileId is not None: 1780 ↛ 1782line 1780 didn't jump to line 1782, because the condition on line 1780 was never false
1781 self.close(treeId, fileId)
1782 self.disconnectTree(treeId)
1784 return True
1786 def remove(self, shareName, pathName, password = None):
1787 # ToDo: Handle situations where share is password protected
1788 pathName = pathName.replace('/', '\\')
1789 pathName = ntpath.normpath(pathName)
1790 if len(pathName) > 0 and pathName[0] == '\\':
1791 pathName = pathName[1:]
1793 treeId = self.connectTree(shareName)
1795 fileId = None
1796 try:
1797 fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
1798 finally:
1799 if fileId is not None: 1799 ↛ 1801line 1799 didn't jump to line 1801, because the condition on line 1799 was never false
1800 self.close(treeId, fileId)
1801 self.disconnectTree(treeId)
1803 return True
1805 def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
1806 createContexts = None
1808 if self.isSnapshotRequest(path): 1808 ↛ 1809line 1808 didn't jump to line 1809, because the condition on line 1808 was never true
1809 createContexts = []
1810 path, ctx = self.timestampForSnapshot(path)
1811 createContexts.append(ctx)
1813 # ToDo: Handle situations where share is password protected
1814 path = path.replace('/', '\\')
1815 path = ntpath.normpath(path)
1816 if len(path) > 0 and path[0] == '\\':
1817 path = path[1:]
1819 treeId = self.connectTree(shareName)
1820 fileId = None
1821 from impacket import smb
1822 try:
1823 fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0, createContexts=createContexts)
1824 res = self.queryInfo(treeId, fileId)
1825 fileInfo = smb.SMBQueryFileStandardInfo(res)
1826 fileSize = fileInfo['EndOfFile']
1827 if (fileSize-offset) < self._Connection['MaxReadSize']:
1828 # Skip reading 0 bytes files.
1829 if (fileSize-offset) > 0: 1829 ↛ 1841line 1829 didn't jump to line 1841, because the condition on line 1829 was never false
1830 data = self.read(treeId, fileId, offset, fileSize-offset)
1831 callback(data)
1832 else:
1833 written = 0
1834 toBeRead = fileSize-offset
1835 while written < toBeRead:
1836 data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize'])
1837 written += len(data)
1838 offset += len(data)
1839 callback(data)
1840 finally:
1841 if fileId is not None: 1841 ↛ 1843line 1841 didn't jump to line 1843, because the condition on line 1841 was never false
1842 self.close(treeId, fileId)
1843 self.disconnectTree(treeId)
1845 def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE):
1846 # ToDo: Handle situations where share is password protected
1847 path = path.replace('/', '\\')
1848 path = ntpath.normpath(path)
1849 if len(path) > 0 and path[0] == '\\': 1849 ↛ 1852line 1849 didn't jump to line 1852, because the condition on line 1849 was never false
1850 path = path[1:]
1852 treeId = self.connectTree(shareName)
1853 fileId = None
1854 try:
1855 fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
1856 finished = False
1857 writeOffset = offset
1858 while not finished: 1858 ↛ 1865line 1858 didn't jump to line 1865, because the condition on line 1858 was never false
1859 data = callback(self._Connection['MaxWriteSize'])
1860 if len(data) == 0:
1861 break
1862 written = self.write(treeId, fileId, data, writeOffset, len(data))
1863 writeOffset += written
1864 finally:
1865 if fileId is not None: 1865 ↛ 1867line 1865 didn't jump to line 1867, because the condition on line 1865 was never false
1866 self.close(treeId, fileId)
1867 self.disconnectTree(treeId)
1869 def waitNamedPipe(self, treeId, pipename, timeout = 5):
1870 pipename = ntpath.basename(pipename)
1871 if (treeId in self._Session['TreeConnectTable']) is False:
1872 raise SessionError(STATUS_INVALID_PARAMETER)
1873 if len(pipename) > 0xffff:
1874 raise SessionError(STATUS_INVALID_PARAMETER)
1876 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()
1877 pipeWait['Timeout'] = timeout*100000
1878 pipeWait['NameLength'] = len(pipename)*2
1879 pipeWait['TimeoutSpecified'] = 1
1880 pipeWait['Name'] = pipename.encode('utf-16le')
1882 return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0)
1884 def getIOCapabilities(self):
1885 res = dict()
1887 res['MaxReadSize'] = self._Connection['MaxReadSize']
1888 res['MaxWriteSize'] = self._Connection['MaxWriteSize']
1889 return res
1892 ######################################################################
1893 # Backward compatibility functions and alias for SMB1 and DCE Transports
1894 # NOTE: It is strongly recommended not to use these commands
1895 # when implementing new client calls.
1896 get_server_name = getServerName
1897 get_client_name = getClientName
1898 get_server_domain = getServerDomain
1899 get_server_dns_domain_name = getServerDNSDomainName
1900 get_server_dns_host_name = getServerDNSHostName
1901 get_remote_name = getRemoteName
1902 set_remote_name = setRemoteName
1903 get_remote_host = getServerIP
1904 get_server_os = getServerOS
1905 get_server_os_major = getServerOSMajor
1906 get_server_os_minor = getServerOSMinor
1907 get_server_os_build = getServerOSBuild
1908 tree_connect_andx = connectTree
1909 tree_connect = connectTree
1910 connect_tree = connectTree
1911 disconnect_tree = disconnectTree
1912 set_timeout = setTimeout
1913 use_timeout = useTimeout
1914 stor_file = storeFile
1915 retr_file = retrieveFile
1916 list_path = listPath
1918 def close_session(self):
1919 if self._NetBIOSSession: 1919 ↛ exitline 1919 didn't return from function 'close_session', because the condition on line 1919 was never false
1920 self._NetBIOSSession.close()
1921 self._NetBIOSSession = None
1923 def doesSupportNTLMv2(self):
1924 # Always true :P
1925 return True
1927 def is_login_required(self):
1928 # Always true :P
1929 return True
1931 def is_signing_required(self):
1932 return self._Connection['RequireSigning']
1934 def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None):
1935 if len(fileName) > 0 and fileName[0] == '\\':
1936 fileName = fileName[1:]
1938 if cmd is not None:
1939 from impacket import smb
1940 ntCreate = smb.SMBCommand(data = cmd.getData())
1941 params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters'])
1942 return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'],
1943 params['CreateOptions'], params['Disposition'], params['FileAttributes'],
1944 params['Impersonation'], params['SecurityFlags'])
1946 else:
1947 return self.create(treeId, fileName,
1948 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
1949 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,
1950 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )
1952 def get_socket(self):
1953 return self._NetBIOSSession.get_socket()
1956 def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
1957 # ToDo: Handle the custom smb_packet situation
1958 return self.write(tid, fid, data, offset, len(data))
1960 def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0):
1961 return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer)
1963 def TransactNamedPipeRecv(self):
1964 ans = self.recvSMB()
1966 if ans.isValidAnswer(STATUS_SUCCESS):
1967 smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
1968 return smbIoctlResponse['Buffer']
1971 def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
1972 # ToDo: Handle the custom smb_packet situation
1973 if max_size is None: 1973 ↛ 1974line 1973 didn't jump to line 1974, because the condition on line 1973 was never true
1974 max_size = self._Connection['MaxReadSize']
1975 return self.read(tid, fid, offset, max_size, wait_answer)
1977 def list_shared(self):
1978 # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED
1979 raise SessionError(STATUS_NOT_IMPLEMENTED)
1981 def open_andx(self, tid, fileName, open_mode, desired_access):
1982 # ToDo Return all the attributes of the file
1983 if len(fileName) > 0 and fileName[0] == '\\':
1984 fileName = fileName[1:]
1986 fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0)
1987 return fileId, 0, 0, 0, 0, 0, 0, 0, 0
1989 def set_session_key(self, signingKey):
1990 self._Session['SessionKey'] = signingKey
1991 self._Session['SigningActivated'] = True
1992 self._Session['SigningRequired'] = True
1994 def set_hostname_validation(self, validate, accept_empty, hostname):
1995 self._strict_hostname_validation = validate
1996 self._validation_allow_absent = accept_empty
1997 self._accepted_hostname = hostname
1999 def perform_hostname_validation(self):
2000 if self._Session['ServerName'] == '':
2001 if not self._validation_allow_absent:
2002 raise self.HostnameValidationException('Hostname was not supplied by target host and absent validation is disallowed')
2003 return
2004 if self._Session['ServerName'].lower() != self._accepted_hostname.lower() and self._Session['ServerDNSHostName'].lower() != self._accepted_hostname.lower():
2005 raise self.HostnameValidationException('Supplied hostname %s does not match reported hostnames %s or %s' %
2006 (self._accepted_hostname.lower(), self._Session['ServerName'].lower(), self._Session['ServerDNSHostName'].lower()))