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