1# Copyright (c) 2003-2016 CORE Security Technologies 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# [ ] 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# 27 28import socket 29import ntpath 30import random 31import string 32import struct 33from binascii import a2b_hex 34from contextlib import contextmanager 35 36from impacket import nmb, ntlm, uuid, crypto, LOG 37from impacket.smb3structs import * 38from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \ 39 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES 40from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp 41 42 43# For signing 44import hashlib, hmac, copy 45 46# Structs to be used 47TREE_CONNECT = { 48 'ShareName' : '', 49 'TreeConnectId' : 0, 50 'Session' : 0, 51 'IsDfsShare' : False, 52 # If the client implements the SMB 3.0 dialect, 53 # the client MUST also implement the following 54 'IsCAShare' : False, 55 'EncryptData' : False, 56 'IsScaleoutShare' : False, 57 # Outside the protocol 58 'NumberOfUses' : 0, 59} 60 61FILE = { 62 'OpenTable' : [], 63 'LeaseKey' : '', 64 'LeaseState' : 0, 65 'LeaseEpoch' : 0, 66} 67 68OPEN = { 69 'FileID' : '', 70 'TreeConnect' : 0, 71 'Connection' : 0, # Not Used 72 'Oplocklevel' : 0, 73 'Durable' : False, 74 'FileName' : '', 75 'ResilientHandle' : False, 76 'LastDisconnectTime' : 0, 77 'ResilientTimeout' : 0, 78 'OperationBuckets' : [], 79 # If the client implements the SMB 3.0 dialect, 80 # the client MUST implement the following 81 'CreateGuid' : '', 82 'IsPersistent' : False, 83 'DesiredAccess' : '', 84 'ShareMode' : 0, 85 'CreateOption' : '', 86 'FileAttributes' : '', 87 'CreateDisposition' : '', 88} 89 90REQUEST = { 91 'CancelID' : '', 92 'Message' : '', 93 'Timestamp' : 0, 94} 95 96CHANNEL = { 97 'SigningKey' : '', 98 'Connection' : 0, 99} 100 101 102class SessionError(Exception): 103 def __init__( self, error = 0, packet=0): 104 Exception.__init__(self) 105 self.error = error 106 self.packet = packet 107 108 def get_error_code( self ): 109 return self.error 110 111 def get_error_packet( self ): 112 return self.packet 113 114 def __str__( self ): 115 return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error]) 116 117 118class SMB3: 119 def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=60, UDP = 0, preferredDialect = None, session = None): 120 121 # [MS-SMB2] Section 3 122 self.RequireMessageSigning = False # 123 self.ConnectionTable = {} 124 self.GlobalFileTable = {} 125 self.ClientGuid = ''.join([random.choice(string.letters) for i in range(16)]) 126 # Only for SMB 3.0 127 self.EncryptionAlgorithmList = ['AES-CCM'] 128 self.MaxDialect = [] 129 self.RequireSecureNegotiate = False 130 131 # Per Transport Connection Data 132 self._Connection = { 133 # Indexed by SessionID 134 #'SessionTable' : {}, 135 # Indexed by MessageID 136 'OutstandingRequests' : {}, 137 'OutstandingResponses' : {}, # 138 'SequenceWindow' : 0, # 139 'GSSNegotiateToken' : '', # 140 'MaxTransactSize' : 0, # 141 'MaxReadSize' : 0, # 142 'MaxWriteSize' : 0, # 143 'ServerGuid' : '', # 144 'RequireSigning' : False, # 145 'ServerName' : '', # 146 # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST 147 # also implement the following 148 'Dialect' : '', # 149 'SupportsFileLeasing' : False, # 150 'SupportsMultiCredit' : False, # 151 # If the client implements the SMB 3.0 dialect, 152 # it MUST also implement the following 153 'SupportsDirectoryLeasing' : False, # 154 'SupportsMultiChannel' : False, # 155 'SupportsPersistentHandles': False, # 156 'SupportsEncryption' : False, # 157 'ClientCapabilities' : 0, 158 'ServerCapabilities' : 0, # 159 'ClientSecurityMode' : 0, # 160 'ServerSecurityMode' : 0, # 161 # Outside the protocol 162 'ServerIP' : '', # 163 } 164 165 self._Session = { 166 'SessionID' : 0, # 167 'TreeConnectTable' : {}, # 168 'SessionKey' : '', # 169 'SigningRequired' : False, # 170 'Connection' : 0, # 171 'UserCredentials' : '', # 172 'OpenTable' : {}, # 173 # If the client implements the SMB 3.0 dialect, 174 # it MUST also implement the following 175 'ChannelList' : [], 176 'ChannelSequence' : 0, 177 #'EncryptData' : False, 178 'EncryptData' : True, 179 'EncryptionKey' : '', 180 'DecryptionKey' : '', 181 'SigningKey' : '', 182 'ApplicationKey' : '', 183 # Outside the protocol 184 'SessionFlags' : 0, # 185 'ServerName' : '', # 186 'ServerDomain' : '', # 187 'ServerDNSDomainName' : '', # 188 'ServerOS' : '', # 189 'SigningActivated' : False, # 190 } 191 192 self.SMB_PACKET = SMB2Packet 193 194 self._timeout = timeout 195 self._Connection['ServerIP'] = remote_host 196 self._NetBIOSSession = None 197 198 self.__userName = '' 199 self.__password = '' 200 self.__domain = '' 201 self.__lmhash = '' 202 self.__nthash = '' 203 self.__kdc = '' 204 self.__aesKey = '' 205 self.__TGT = None 206 self.__TGS = None 207 208 if sess_port == 445 and remote_name == '*SMBSERVER': 209 self._Connection['ServerName'] = remote_host 210 else: 211 self._Connection['ServerName'] = remote_name 212 213 if session is None: 214 if not my_name: 215 my_name = socket.gethostname() 216 i = string.find(my_name, '.') 217 if i > -1: 218 my_name = my_name[:i] 219 220 if UDP: 221 self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 222 else: 223 self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 224 225 self.negotiateSession(preferredDialect) 226 else: 227 self._NetBIOSSession = session 228 # We should increase the SequenceWindow since a packet was already received. 229 self._Connection['SequenceWindow'] += 1 230 # Let's negotiate again using the same connection 231 self.negotiateSession(preferredDialect) 232 233 def printStatus(self): 234 print "CONNECTION" 235 for i in self._Connection.items(): 236 print "%-40s : %s" % i 237 print 238 print "SESSION" 239 for i in self._Session.items(): 240 print "%-40s : %s" % i 241 242 def getServerName(self): 243 return self._Session['ServerName'] 244 245 def getServerIP(self): 246 return self._Connection['ServerIP'] 247 248 def getServerDomain(self): 249 return self._Session['ServerDomain'] 250 251 def getServerDNSDomainName(self): 252 return self._Session['ServerDNSDomainName'] 253 254 def getServerOS(self): 255 return self._Session['ServerOS'] 256 257 def getServerOSMajor(self): 258 return self._Session['ServerOSMajor'] 259 260 def getServerOSMinor(self): 261 return self._Session['ServerOSMinor'] 262 263 def getServerOSBuild(self): 264 return self._Session['ServerOSBuild'] 265 266 def isGuestSession(self): 267 return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST 268 269 def setTimeout(self, timeout): 270 self._timeout = timeout 271 272 @contextmanager 273 def useTimeout(self, timeout): 274 prev_timeout = self.getTimeout(timeout) 275 try: 276 yield 277 finally: 278 self.setTimeout(prev_timeout) 279 280 def getDialect(self): 281 return self._Connection['Dialect'] 282 283 284 def signSMB(self, packet): 285 packet['Signature'] = '\x00'*16 286 if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002: 287 if len(self._Session['SessionKey']) > 0: 288 signature = hmac.new(self._Session['SessionKey'], str(packet), hashlib.sha256).digest() 289 packet['Signature'] = signature[:16] 290 else: 291 if len(self._Session['SessionKey']) > 0: 292 p = str(packet) 293 signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p)) 294 packet['Signature'] = signature 295 296 def sendSMB(self, packet): 297 # The idea here is to receive multiple/single commands and create a compound request, and send it 298 # Should return the MessageID for later retrieval. Implement compounded related requests. 299 300 # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or 301 # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the 302 # SMB2 header to Session.ChannelSequence 303 304 # Check this is not a CANCEL request. If so, don't consume sequece numbers 305 if packet['Command'] is not SMB2_CANCEL: 306 packet['MessageID'] = self._Connection['SequenceWindow'] 307 self._Connection['SequenceWindow'] += 1 308 packet['SessionID'] = self._Session['SessionID'] 309 310 # Default the credit charge to 1 unless set by the caller 311 if packet.fields.has_key('CreditCharge') is False: 312 packet['CreditCharge'] = 1 313 314 # Standard credit request after negotiating protocol 315 if self._Connection['SequenceWindow'] > 3: 316 packet['CreditRequestResponse'] = 127 317 318 messageId = packet['MessageID'] 319 320 if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2: 321 if packet['TreeID'] > 0 and self._Session['TreeConnectTable'].has_key(packet['TreeID']) is True: 322 if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False: 323 packet['Flags'] = SMB2_FLAGS_SIGNED 324 self.signSMB(packet) 325 elif packet['TreeID'] == 0: 326 packet['Flags'] = SMB2_FLAGS_SIGNED 327 self.signSMB(packet) 328 329 if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True): 330 plainText = str(packet) 331 transformHeader = SMB2_TRANSFORM_HEADER() 332 transformHeader['Nonce'] = ''.join([random.choice(string.letters) for i in range(11)]) 333 transformHeader['OriginalMessageSize'] = len(plainText) 334 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM 335 transformHeader['SessionID'] = self._Session['SessionID'] 336 from Crypto.Cipher import AES 337 try: 338 AES.MODE_CCM 339 except: 340 LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ") 341 raise 342 cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, transformHeader['Nonce']) 343 cipher.update(str(transformHeader)[20:]) 344 cipherText = cipher.encrypt(plainText) 345 transformHeader['Signature'] = cipher.digest() 346 packet = str(transformHeader) + cipherText 347 348 self._NetBIOSSession.send_packet(str(packet)) 349 return messageId 350 351 def recvSMB(self, packetID = None): 352 # First, verify we don't have the packet already 353 if self._Connection['OutstandingResponses'].has_key(packetID): 354 return self._Connection['OutstandingResponses'].pop(packetID) 355 356 data = self._NetBIOSSession.recv_packet(self._timeout) 357 358 if data.get_trailer().startswith('\xfdSMB'): 359 # Packet is encrypted 360 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 361 from Crypto.Cipher import AES 362 try: 363 AES.MODE_CCM 364 except: 365 LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ") 366 raise 367 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 368 cipher.update(str(transformHeader)[20:]) 369 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 370 #cipher.verify(transformHeader['Signature']) 371 packet = SMB2Packet(plainText) 372 else: 373 # In all SMB dialects for a response this field is interpreted as the Status field. 374 # This field can be set to any value. For a list of valid status codes, 375 # see [MS-ERREF] section 2.3. 376 packet = SMB2Packet(data.get_trailer()) 377 378 # Loop while we receive pending requests 379 if packet['Status'] == STATUS_PENDING: 380 status = STATUS_PENDING 381 while status == STATUS_PENDING: 382 data = self._NetBIOSSession.recv_packet(self._timeout) 383 if data.get_trailer().startswith('\xfeSMB'): 384 packet = SMB2Packet(data.get_trailer()) 385 else: 386 # Packet is encrypted 387 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 388 from Crypto.Cipher import AES 389 try: 390 AES.MODE_CCM 391 except: 392 LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ") 393 raise 394 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 395 cipher.update(str(transformHeader)[20:]) 396 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 397 #cipher.verify(transformHeader['Signature']) 398 packet = SMB2Packet(plainText) 399 status = packet['Status'] 400 401 if packet['MessageID'] == packetID or packetID is None: 402 # if self._Session['SigningRequired'] is True: 403 # self.signSMB(packet) 404 # Let's update the sequenceWindow based on the CreditsCharged 405 self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1) 406 return packet 407 else: 408 self._Connection['OutstandingResponses'][packet['MessageID']] = packet 409 return self.recvSMB(packetID) 410 411 def negotiateSession(self, preferredDialect = None): 412 packet = self.SMB_PACKET() 413 packet['Command'] = SMB2_NEGOTIATE 414 negSession = SMB2Negotiate() 415 416 negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 417 if self.RequireMessageSigning is True: 418 negSession['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED 419 negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION 420 negSession['ClientGuid'] = self.ClientGuid 421 if preferredDialect is not None: 422 negSession['Dialects'] = [preferredDialect] 423 else: 424 negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30] 425 negSession['DialectCount'] = len(negSession['Dialects']) 426 packet['Data'] = negSession 427 428 # Storing this data for later use 429 self._Connection['ClientSecurityMode'] = negSession['SecurityMode'] 430 self._Connection['Capabilities'] = negSession['Capabilities'] 431 432 packetID = self.sendSMB(packet) 433 ans = self.recvSMB(packetID) 434 if ans.isValidAnswer(STATUS_SUCCESS): 435 # ToDo this: 436 # If the DialectRevision in the SMB2 NEGOTIATE Response is 0x02FF, the client MUST issue a new 437 # SMB2 NEGOTIATE request as described in section 3.2.4.2.2.2 with the only exception 438 # that the client MUST allocate sequence number 1 from Connection.SequenceWindow, and MUST set 439 # MessageId field of the SMB2 header to 1. Otherwise, the client MUST proceed as follows. 440 negResp = SMB2Negotiate_Response(ans['Data']) 441 self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize']) 442 self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize']) 443 self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize']) 444 self._Connection['ServerGuid'] = negResp['ServerGuid'] 445 self._Connection['GSSNegotiateToken'] = negResp['Buffer'] 446 self._Connection['Dialect'] = negResp['DialectRevision'] 447 if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED: 448 self._Connection['RequireSigning'] = True 449 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING: 450 self._Connection['SupportsFileLeasing'] = True 451 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU: 452 self._Connection['SupportsMultiCredit'] = True 453 454 if self._Connection['Dialect'] == SMB2_DIALECT_30: 455 # Switching to the right packet format 456 self.SMB_PACKET = SMB3Packet 457 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING: 458 self._Connection['SupportsDirectoryLeasing'] = True 459 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL: 460 self._Connection['SupportsMultiChannel'] = True 461 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES: 462 self._Connection['SupportsPersistentHandles'] = True 463 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION: 464 self._Connection['SupportsEncryption'] = True 465 466 self._Connection['ServerCapabilities'] = negResp['Capabilities'] 467 self._Connection['ServerSecurityMode'] = negResp['SecurityMode'] 468 469 def getCredentials(self): 470 return ( 471 self.__userName, 472 self.__password, 473 self.__domain, 474 self.__lmhash, 475 self.__nthash, 476 self.__aesKey, 477 self.__TGT, 478 self.__TGS) 479 480 def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None): 481 # If TGT or TGS are specified, they are in the form of: 482 # TGS['KDC_REP'] = the response from the server 483 # TGS['cipher'] = the cipher used 484 # TGS['sessionKey'] = the sessionKey 485 # If we have hashes, normalize them 486 if lmhash != '' or nthash != '': 487 if len(lmhash) % 2: lmhash = '0%s' % lmhash 488 if len(nthash) % 2: nthash = '0%s' % nthash 489 try: # just in case they were converted already 490 lmhash = a2b_hex(lmhash) 491 nthash = a2b_hex(nthash) 492 except: 493 pass 494 495 self.__userName = user 496 self.__password = password 497 self.__domain = domain 498 self.__lmhash = lmhash 499 self.__nthash = nthash 500 self.__kdc = kdcHost 501 self.__aesKey = aesKey 502 self.__TGT = TGT 503 self.__TGS = TGS 504 505 sessionSetup = SMB2SessionSetup() 506 if self.RequireMessageSigning is True: 507 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 508 else: 509 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 510 511 sessionSetup['Flags'] = 0 512 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 513 514 # Importing down here so pyasn1 is not required if kerberos is not used. 515 from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set 516 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS 517 from impacket.krb5 import constants 518 from impacket.krb5.types import Principal, KerberosTime, Ticket 519 from pyasn1.codec.der import decoder, encoder 520 import datetime 521 522 # First of all, we need to get a TGT for the user 523 userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 524 if TGT is None: 525 if TGS is None: 526 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) 527 else: 528 tgt = TGT['KDC_REP'] 529 cipher = TGT['cipher'] 530 sessionKey = TGT['sessionKey'] 531 532 # Save the ticket 533 # If you want, for debugging purposes 534# from impacket.krb5.ccache import CCache 535# ccache = CCache() 536# try: 537# if TGS is None: 538# ccache.fromTGT(tgt, oldSessionKey, sessionKey) 539# else: 540# ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] ) 541# ccache.saveFile('/tmp/ticket.bin') 542# except Exception, e: 543# print e 544# pass 545 546 # Now that we have the TGT, we should ask for a TGS for cifs 547 548 if TGS is None: 549 serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value) 550 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) 551 else: 552 tgs = TGS['KDC_REP'] 553 cipher = TGS['cipher'] 554 sessionKey = TGS['sessionKey'] 555 556 # Let's build a NegTokenInit with a Kerberos REQ_AP 557 558 blob = SPNEGO_NegTokenInit() 559 560 # Kerberos 561 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] 562 563 # Let's extract the ticket from the TGS 564 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] 565 ticket = Ticket() 566 ticket.from_asn1(tgs['ticket']) 567 568 # Now let's build the AP_REQ 569 apReq = AP_REQ() 570 apReq['pvno'] = 5 571 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) 572 573 opts = list() 574 apReq['ap-options'] = constants.encodeFlags(opts) 575 seq_set(apReq,'ticket', ticket.to_asn1) 576 577 authenticator = Authenticator() 578 authenticator['authenticator-vno'] = 5 579 authenticator['crealm'] = domain 580 seq_set(authenticator, 'cname', userName.components_to_asn1) 581 now = datetime.datetime.utcnow() 582 583 authenticator['cusec'] = now.microsecond 584 authenticator['ctime'] = KerberosTime.to_asn1(now) 585 586 encodedAuthenticator = encoder.encode(authenticator) 587 588 # Key Usage 11 589 # AP-REQ Authenticator (includes application authenticator 590 # subkey), encrypted with the application session key 591 # (Section 5.5.1) 592 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) 593 594 apReq['authenticator'] = None 595 apReq['authenticator']['etype'] = cipher.enctype 596 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator 597 598 blob['MechToken'] = encoder.encode(apReq) 599 600 sessionSetup['SecurityBufferLength'] = len(blob) 601 sessionSetup['Buffer'] = blob.getData() 602 603 packet = self.SMB_PACKET() 604 packet['Command'] = SMB2_SESSION_SETUP 605 packet['Data'] = sessionSetup 606 607 packetID = self.sendSMB(packet) 608 ans = self.recvSMB(packetID) 609 if ans.isValidAnswer(STATUS_SUCCESS): 610 self._Session['SessionID'] = ans['SessionID'] 611 self._Session['SigningRequired'] = self._Connection['RequireSigning'] 612 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 613 self._Session['Connection'] = self._NetBIOSSession.get_socket() 614 615 self._Session['SessionKey'] = sessionKey.contents[:16] 616 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30: 617 self._Session['SigningKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCMAC\x00", "SmbSign\x00", 128) 618 619 # Calculate the key derivations for dialect 3.0 620 if self._Session['SigningRequired'] is True: 621 self._Session['SigningActivated'] = True 622 if self._Connection['Dialect'] == SMB2_DIALECT_30: 623 self._Session['ApplicationKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2APP\x00", "SmbRpc\x00", 128) 624 self._Session['EncryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerIn \x00", 128) 625 self._Session['DecryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerOut\x00", 128) 626 627 return True 628 else: 629 # We clean the stuff we used in case we want to authenticate again 630 # within the same connection 631 self._Session['UserCredentials'] = '' 632 self._Session['Connection'] = 0 633 self._Session['SessionID'] = 0 634 self._Session['SigningRequired'] = False 635 self._Session['SigningKey'] = '' 636 self._Session['SessionKey'] = '' 637 self._Session['SigningActivated'] = False 638 raise 639 640 641 def login(self, user, password, domain = '', lmhash = '', nthash = ''): 642 # If we have hashes, normalize them 643 if lmhash != '' or nthash != '': 644 if len(lmhash) % 2: lmhash = '0%s' % lmhash 645 if len(nthash) % 2: nthash = '0%s' % nthash 646 try: # just in case they were converted already 647 lmhash = a2b_hex(lmhash) 648 nthash = a2b_hex(nthash) 649 except: 650 pass 651 652 self.__userName = user 653 self.__password = password 654 self.__domain = domain 655 self.__lmhash = lmhash 656 self.__nthash = nthash 657 self.__aesKey = '' 658 self.__TGT = None 659 self.__TGS = None 660 661 sessionSetup = SMB2SessionSetup() 662 if self.RequireMessageSigning is True: 663 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 664 else: 665 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 666 667 sessionSetup['Flags'] = 0 668 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 669 670 # Let's build a NegTokenInit with the NTLMSSP 671 # TODO: In the future we should be able to choose different providers 672 673 blob = SPNEGO_NegTokenInit() 674 675 # NTLMSSP 676 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 677 auth = ntlm.getNTLMSSPType1('','', self._Connection['RequireSigning']) 678 blob['MechToken'] = str(auth) 679 680 sessionSetup['SecurityBufferLength'] = len(blob) 681 sessionSetup['Buffer'] = blob.getData() 682 683 # ToDo: 684 # If this authentication is for establishing an alternative channel for an existing Session, as specified 685 # in section 3.2.4.1.7, the client MUST also set the following values: 686 # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new 687 # channel being established. 688 # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field. 689 # The PreviousSessionId field MUST be set to zero. 690 691 packet = self.SMB_PACKET() 692 packet['Command'] = SMB2_SESSION_SETUP 693 packet['Data'] = sessionSetup 694 695 packetID = self.sendSMB(packet) 696 ans = self.recvSMB(packetID) 697 if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED): 698 self._Session['SessionID'] = ans['SessionID'] 699 self._Session['SigningRequired'] = self._Connection['RequireSigning'] 700 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 701 self._Session['Connection'] = self._NetBIOSSession.get_socket() 702 sessionSetupResponse = SMB2SessionSetup_Response(ans['Data']) 703 respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer']) 704 705 # Let's parse some data and keep it to ourselves in case it is asked 706 ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken']) 707 if ntlmChallenge['TargetInfoFields_len'] > 0: 708 av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']]) 709 if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: 710 try: 711 self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') 712 except: 713 # For some reason, we couldn't decode Unicode here.. silently discard the operation 714 pass 715 if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: 716 try: 717 if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): 718 self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le') 719 except: 720 # For some reason, we couldn't decode Unicode here.. silently discard the operation 721 pass 722 if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: 723 try: 724 self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le') 725 except: 726 # For some reason, we couldn't decode Unicode here.. silently discard the operation 727 pass 728 729 # Parse Version to know the target Operating system name. Not provided elsewhere anymore 730 if ntlmChallenge.fields.has_key('Version'): 731 version = ntlmChallenge['Version'] 732 733 if len(version) >= 4: 734 self._Session['ServerOS'] = "Windows %d.%d Build %d" % (ord(version[0]), ord(version[1]), struct.unpack('<H',version[2:4])[0]) 735 self._Session["ServerOSMajor"] = ord(version[0]) 736 self._Session["ServerOSMinor"] = ord(version[1]) 737 self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0] 738 739 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash) 740 741 if exportedSessionKey is not None: 742 self._Session['SessionKey'] = exportedSessionKey 743 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30: 744 self._Session['SigningKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCMAC\x00", "SmbSign\x00", 128) 745 746 respToken2 = SPNEGO_NegTokenResp() 747 respToken2['ResponseToken'] = str(type3) 748 749 # Reusing the previous structure 750 sessionSetup['SecurityBufferLength'] = len(respToken2) 751 sessionSetup['Buffer'] = respToken2.getData() 752 753 packetID = self.sendSMB(packet) 754 packet = self.recvSMB(packetID) 755 try: 756 if packet.isValidAnswer(STATUS_SUCCESS): 757 sessionSetupResponse = SMB2SessionSetup_Response(packet['Data']) 758 self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags'] 759 760 # Calculate the key derivations for dialect 3.0 761 if self._Session['SigningRequired'] is True: 762 self._Session['SigningActivated'] = True 763 if self._Connection['Dialect'] == SMB2_DIALECT_30: 764 self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2APP\x00", "SmbRpc\x00", 128) 765 self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerIn \x00", 128) 766 self._Session['DecryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerOut\x00", 128) 767 768 return True 769 except: 770 # We clean the stuff we used in case we want to authenticate again 771 # within the same connection 772 self._Session['UserCredentials'] = '' 773 self._Session['Connection'] = 0 774 self._Session['SessionID'] = 0 775 self._Session['SigningRequired'] = False 776 self._Session['SigningKey'] = '' 777 self._Session['SessionKey'] = '' 778 self._Session['SigningActivated'] = False 779 raise 780 781 def connectTree(self, share): 782 783 # Just in case this came with the full path (maybe an SMB1 client), let's just leave 784 # the sharename, we'll take care of the rest 785 786 #print self._Session['TreeConnectTable'] 787 share = share.split('\\')[-1] 788 if self._Session['TreeConnectTable'].has_key(share): 789 # Already connected, no need to reconnect 790 treeEntry = self._Session['TreeConnectTable'][share] 791 treeEntry['NumberOfUses'] += 1 792 self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1 793 return treeEntry['TreeConnectId'] 794 795 #path = share 796 try: 797 _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0] 798 remoteHost = sockaddr[0] 799 except: 800 remoteHost = self._Connection['ServerIP'] 801 path = '\\\\' + remoteHost + '\\' +share 802 803 treeConnect = SMB2TreeConnect() 804 treeConnect['Buffer'] = path.encode('utf-16le') 805 treeConnect['PathLength'] = len(path)*2 806 807 packet = self.SMB_PACKET() 808 packet['Command'] = SMB2_TREE_CONNECT 809 packet['Data'] = treeConnect 810 packetID = self.sendSMB(packet) 811 packet = self.recvSMB(packetID) 812 if packet.isValidAnswer(STATUS_SUCCESS): 813 treeConnectResponse = SMB2TreeConnect_Response(packet['Data']) 814 treeEntry = copy.deepcopy(TREE_CONNECT) 815 treeEntry['ShareName'] = share 816 treeEntry['TreeConnectId'] = packet['TreeID'] 817 treeEntry['Session'] = packet['SessionID'] 818 treeEntry['NumberOfUses'] += 1 819 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS: 820 treeEntry['IsDfsShare'] = True 821 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY: 822 treeEntry['IsCAShare'] = True 823 824 if self._Connection['Dialect'] == SMB2_DIALECT_30: 825 if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA): 826 treeEntry['EncryptData'] = True 827 # ToDo: This and what follows 828 # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a 829 # decryption key as specified in section 3.1.4.2, by providing the following inputs and store 830 # them in Session.EncryptionKey and Session.DecryptionKey: 831 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT: 832 treeEntry['IsScaleoutShare'] = True 833 834 self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry 835 self._Session['TreeConnectTable'][share] = treeEntry 836 837 return packet['TreeID'] 838 839 def disconnectTree(self, treeId): 840 if self._Session['TreeConnectTable'].has_key(treeId) is False: 841 raise SessionError(STATUS_INVALID_PARAMETER) 842 843 if self._Session['TreeConnectTable'].has_key(treeId): 844 # More than 1 use? descrease it and return, if not, send the packet 845 if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1: 846 treeEntry = self._Session['TreeConnectTable'][treeId] 847 treeEntry['NumberOfUses'] -= 1 848 self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1 849 return True 850 851 packet = self.SMB_PACKET() 852 packet['Command'] = SMB2_TREE_DISCONNECT 853 packet['TreeID'] = treeId 854 treeDisconnect = SMB2TreeDisconnect() 855 packet['Data'] = treeDisconnect 856 packetID = self.sendSMB(packet) 857 packet = self.recvSMB(packetID) 858 if packet.isValidAnswer(STATUS_SUCCESS): 859 shareName = self._Session['TreeConnectTable'][treeId]['ShareName'] 860 del(self._Session['TreeConnectTable'][shareName]) 861 del(self._Session['TreeConnectTable'][treeId]) 862 return True 863 864 def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None): 865 if self._Session['TreeConnectTable'].has_key(treeId) is False: 866 raise SessionError(STATUS_INVALID_PARAMETER) 867 868 fileName = string.replace(fileName, '/', '\\') 869 if len(fileName) > 0: 870 fileName = ntpath.normpath(fileName) 871 if fileName[0] == '\\': 872 fileName = fileName[1:] 873 874 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 875 pathName = fileName 876 else: 877 pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName 878 879 fileEntry = copy.deepcopy(FILE) 880 fileEntry['LeaseKey'] = uuid.generate() 881 fileEntry['LeaseState'] = SMB2_LEASE_NONE 882 self.GlobalFileTable[pathName] = fileEntry 883 884 if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True: 885 # Is this file NOT on the root directory? 886 if len(fileName.split('\\')) > 2: 887 parentDir = ntpath.dirname(pathName) 888 if self.GlobalFileTable.has_key(parentDir): 889 LOG.critical("Don't know what to do now! :-o") 890 raise 891 else: 892 parentEntry = copy.deepcopy(FILE) 893 parentEntry['LeaseKey'] = uuid.generate() 894 parentEntry['LeaseState'] = SMB2_LEASE_NONE 895 self.GlobalFileTable[parentDir] = parentEntry 896 897 packet = self.SMB_PACKET() 898 packet['Command'] = SMB2_CREATE 899 packet['TreeID'] = treeId 900 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 901 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS 902 903 smb2Create = SMB2Create() 904 smb2Create['SecurityFlags'] = 0 905 smb2Create['RequestedOplockLevel'] = oplockLevel 906 smb2Create['ImpersonationLevel'] = impersonationLevel 907 smb2Create['DesiredAccess'] = desiredAccess 908 smb2Create['FileAttributes'] = fileAttributes 909 smb2Create['ShareAccess'] = shareMode 910 smb2Create['CreateDisposition'] = creationDisposition 911 smb2Create['CreateOptions'] = creationOptions 912 913 smb2Create['NameLength'] = len(fileName)*2 914 if fileName != '': 915 smb2Create['Buffer'] = fileName.encode('utf-16le') 916 else: 917 smb2Create['Buffer'] = '\x00' 918 919 if createContexts is not None: 920 smb2Create['Buffer'] += createContexts 921 smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength'] 922 smb2Create['CreateContextsLength'] = len(createContexts) 923 else: 924 smb2Create['CreateContextsOffset'] = 0 925 smb2Create['CreateContextsLength'] = 0 926 927 packet['Data'] = smb2Create 928 929 packetID = self.sendSMB(packet) 930 ans = self.recvSMB(packetID) 931 if ans.isValidAnswer(STATUS_SUCCESS): 932 createResponse = SMB2Create_Response(ans['Data']) 933 934 openFile = copy.deepcopy(OPEN) 935 openFile['FileID'] = createResponse['FileID'] 936 openFile['TreeConnect'] = treeId 937 openFile['Oplocklevel'] = oplockLevel 938 openFile['Durable'] = False 939 openFile['ResilientHandle'] = False 940 openFile['LastDisconnectTime'] = 0 941 openFile['FileName'] = pathName 942 943 # ToDo: Complete the OperationBuckets 944 if self._Connection['Dialect'] == SMB2_DIALECT_30: 945 openFile['DesiredAccess'] = oplockLevel 946 openFile['ShareMode'] = oplockLevel 947 openFile['CreateOptions'] = oplockLevel 948 openFile['FileAttributes'] = oplockLevel 949 openFile['CreateDisposition'] = oplockLevel 950 951 # ToDo: Process the contexts 952 self._Session['OpenTable'][str(createResponse['FileID'])] = openFile 953 954 # The client MUST generate a handle for the Open, and it MUST 955 # return success and the generated handle to the calling application. 956 # In our case, str(FileID) 957 return str(createResponse['FileID']) 958 959 def close(self, treeId, fileId): 960 if self._Session['TreeConnectTable'].has_key(treeId) is False: 961 raise SessionError(STATUS_INVALID_PARAMETER) 962 if self._Session['OpenTable'].has_key(fileId) is False: 963 raise SessionError(STATUS_INVALID_PARAMETER) 964 965 packet = self.SMB_PACKET() 966 packet['Command'] = SMB2_CLOSE 967 packet['TreeID'] = treeId 968 969 smbClose = SMB2Close() 970 smbClose['Flags'] = 0 971 smbClose['FileID'] = fileId 972 973 packet['Data'] = smbClose 974 975 packetID = self.sendSMB(packet) 976 ans = self.recvSMB(packetID) 977 978 if ans.isValidAnswer(STATUS_SUCCESS): 979 del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']]) 980 del(self._Session['OpenTable'][fileId]) 981 982 # ToDo Remove stuff from GlobalFileTable 983 return True 984 985 def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True): 986 # IMPORTANT NOTE: As you can see, this was coded as a recursive function 987 # Hence, you can exhaust the memory pretty easy ( large bytesToRead ) 988 # This function should NOT be used for reading files directly, but another higher 989 # level function should be used that will break the read into smaller pieces 990 991 if self._Session['TreeConnectTable'].has_key(treeId) is False: 992 raise SessionError(STATUS_INVALID_PARAMETER) 993 if self._Session['OpenTable'].has_key(fileId) is False: 994 raise SessionError(STATUS_INVALID_PARAMETER) 995 996 packet = self.SMB_PACKET() 997 packet['Command'] = SMB2_READ 998 packet['TreeID'] = treeId 999 1000 if self._Connection['MaxReadSize'] < bytesToRead: 1001 maxBytesToRead = self._Connection['MaxReadSize'] 1002 else: 1003 maxBytesToRead = bytesToRead 1004 1005 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 1006 packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) / 65536) 1007 else: 1008 maxBytesToRead = min(65536,bytesToRead) 1009 1010 smbRead = SMB2Read() 1011 smbRead['Padding'] = 0x50 1012 smbRead['FileID'] = fileId 1013 smbRead['Length'] = maxBytesToRead 1014 smbRead['Offset'] = offset 1015 packet['Data'] = smbRead 1016 1017 packetID = self.sendSMB(packet) 1018 ans = self.recvSMB(packetID) 1019 1020 if ans.isValidAnswer(STATUS_SUCCESS): 1021 readResponse = SMB2Read_Response(ans['Data']) 1022 retData = readResponse['Buffer'] 1023 if readResponse['DataRemaining'] > 0: 1024 retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer) 1025 return retData 1026 1027 def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True): 1028 # IMPORTANT NOTE: As you can see, this was coded as a recursive function 1029 # Hence, you can exhaust the memory pretty easy ( large bytesToWrite ) 1030 # This function should NOT be used for writing directly to files, but another higher 1031 # level function should be used that will break the writes into smaller pieces 1032 1033 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1034 raise SessionError(STATUS_INVALID_PARAMETER) 1035 if self._Session['OpenTable'].has_key(fileId) is False: 1036 raise SessionError(STATUS_INVALID_PARAMETER) 1037 1038 packet = self.SMB_PACKET() 1039 packet['Command'] = SMB2_WRITE 1040 packet['TreeID'] = treeId 1041 1042 if self._Connection['MaxWriteSize'] < bytesToWrite: 1043 maxBytesToWrite = self._Connection['MaxWriteSize'] 1044 else: 1045 maxBytesToWrite = bytesToWrite 1046 1047 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 1048 packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) / 65536) 1049 else: 1050 maxBytesToWrite = min(65536,bytesToWrite) 1051 1052 smbWrite = SMB2Write() 1053 smbWrite['FileID'] = fileId 1054 smbWrite['Length'] = maxBytesToWrite 1055 smbWrite['Offset'] = offset 1056 smbWrite['WriteChannelInfoOffset'] = 0 1057 smbWrite['Buffer'] = data[:maxBytesToWrite] 1058 packet['Data'] = smbWrite 1059 1060 packetID = self.sendSMB(packet) 1061 if waitAnswer is True: 1062 ans = self.recvSMB(packetID) 1063 else: 1064 return maxBytesToWrite 1065 1066 if ans.isValidAnswer(STATUS_SUCCESS): 1067 writeResponse = SMB2Write_Response(ans['Data']) 1068 bytesWritten = writeResponse['Count'] 1069 if bytesWritten < bytesToWrite: 1070 bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer) 1071 return bytesWritten 1072 1073 def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False): 1074 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1075 raise SessionError(STATUS_INVALID_PARAMETER) 1076 if self._Session['OpenTable'].has_key(fileId) is False: 1077 raise SessionError(STATUS_INVALID_PARAMETER) 1078 1079 packet = self.SMB_PACKET() 1080 packet['Command'] = SMB2_QUERY_DIRECTORY 1081 packet['TreeID'] = treeId 1082 1083 queryDirectory = SMB2QueryDirectory() 1084 queryDirectory['FileInformationClass'] = informationClass 1085 if resumeIndex != 0 : 1086 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED 1087 queryDirectory['FileIndex'] = resumeIndex 1088 queryDirectory['FileID'] = fileId 1089 if maxBufferSize is None: 1090 maxBufferSize = self._Connection['MaxReadSize'] 1091 queryDirectory['OutputBufferLength'] = maxBufferSize 1092 queryDirectory['FileNameLength'] = len(searchString)*2 1093 queryDirectory['Buffer'] = searchString.encode('utf-16le') 1094 1095 packet['Data'] = queryDirectory 1096 1097 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 1098 packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) / 65536) 1099 1100 packetID = self.sendSMB(packet) 1101 ans = self.recvSMB(packetID) 1102 if ans.isValidAnswer(STATUS_SUCCESS): 1103 queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data']) 1104 return queryDirectoryResponse['Buffer'] 1105 1106 def echo(self): 1107 packet = self.SMB_PACKET() 1108 packet['Command'] = SMB2_ECHO 1109 smbEcho = SMB2Echo() 1110 packet['Data'] = smbEcho 1111 packetID = self.sendSMB(packet) 1112 ans = self.recvSMB(packetID) 1113 if ans.isValidAnswer(STATUS_SUCCESS): 1114 return True 1115 1116 def cancel(self, packetID): 1117 packet = self.SMB_PACKET() 1118 packet['Command'] = SMB2_CANCEL 1119 packet['MessageID'] = packetID 1120 1121 smbCancel = SMB2Cancel() 1122 1123 packet['Data'] = smbCancel 1124 self.sendSMB(packet) 1125 1126 def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1): 1127 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1128 raise SessionError(STATUS_INVALID_PARAMETER) 1129 if fileId is None: 1130 fileId = '\xff'*16 1131 else: 1132 if self._Session['OpenTable'].has_key(fileId) is False: 1133 raise SessionError(STATUS_INVALID_PARAMETER) 1134 1135 packet = self.SMB_PACKET() 1136 packet['Command'] = SMB2_IOCTL 1137 packet['TreeID'] = treeId 1138 1139 smbIoctl = SMB2Ioctl() 1140 smbIoctl['FileID'] = fileId 1141 smbIoctl['CtlCode'] = ctlCode 1142 smbIoctl['MaxInputResponse'] = maxInputResponse 1143 smbIoctl['MaxOutputResponse'] = maxOutputResponse 1144 smbIoctl['InputCount'] = len(inputBlob) 1145 if len(inputBlob) == 0: 1146 smbIoctl['InputOffset'] = 0 1147 smbIoctl['Buffer'] = '\x00' 1148 else: 1149 smbIoctl['Buffer'] = inputBlob 1150 smbIoctl['OutputOffset'] = 0 1151 smbIoctl['MaxOutputResponse'] = maxOutputResponse 1152 smbIoctl['Flags'] = flags 1153 1154 packet['Data'] = smbIoctl 1155 1156 packetID = self.sendSMB(packet) 1157 1158 if waitAnswer == 0: 1159 return True 1160 1161 ans = self.recvSMB(packetID) 1162 1163 if ans.isValidAnswer(STATUS_SUCCESS): 1164 smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 1165 return smbIoctlResponse['Buffer'] 1166 1167 def flush(self,treeId, fileId): 1168 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1169 raise SessionError(STATUS_INVALID_PARAMETER) 1170 if self._Session['OpenTable'].has_key(fileId) is False: 1171 raise SessionError(STATUS_INVALID_PARAMETER) 1172 1173 packet = self.SMB_PACKET() 1174 packet['Command'] = SMB2_FLUSH 1175 packet['TreeID'] = treeId 1176 1177 smbFlush = SMB2Flush() 1178 smbFlush['FileID'] = fileId 1179 1180 packet['Data'] = smbFlush 1181 1182 packetID = self.sendSMB(packet) 1183 ans = self.recvSMB(packetID) 1184 1185 if ans.isValidAnswer(STATUS_SUCCESS): 1186 return True 1187 1188 def lock(self, treeId, fileId, locks, lockSequence = 0): 1189 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1190 raise SessionError(STATUS_INVALID_PARAMETER) 1191 if self._Session['OpenTable'].has_key(fileId) is False: 1192 raise SessionError(STATUS_INVALID_PARAMETER) 1193 1194 packet = self.SMB_PACKET() 1195 packet['Command'] = SMB2_LOCK 1196 packet['TreeID'] = treeId 1197 1198 smbLock = SMB2Lock() 1199 smbLock['FileID'] = fileId 1200 smbLock['LockCount'] = len(locks) 1201 smbLock['LockSequence'] = lockSequence 1202 smbLock['Locks'] = ''.join(str(x) for x in locks) 1203 1204 packet['Data'] = smbLock 1205 1206 packetID = self.sendSMB(packet) 1207 ans = self.recvSMB(packetID) 1208 1209 if ans.isValidAnswer(STATUS_SUCCESS): 1210 smbFlushResponse = SMB2Lock_Response(ans['Data']) 1211 return True 1212 1213 # ToDo: 1214 # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST 1215 # do the following: 1216 # The client MUST scan through Open.OperationBuckets and find an element with its Free field 1217 # set to TRUE. If no such element could be found, an implementation-specific error MUST be 1218 # returned to the application. 1219 # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and 1220 # let BucketNumber = BucketIndex +1. 1221 # Set Open.OperationBuckets[BucketIndex].Free = FALSE 1222 # Let the SequenceNumber of the element chosen above be referred to as BucketSequence. 1223 # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) + 1224 # BucketSequence. 1225 # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic. 1226 1227 def logoff(self): 1228 packet = self.SMB_PACKET() 1229 packet['Command'] = SMB2_LOGOFF 1230 1231 smbLogoff = SMB2Logoff() 1232 1233 packet['Data'] = smbLogoff 1234 1235 packetID = self.sendSMB(packet) 1236 ans = self.recvSMB(packetID) 1237 1238 if ans.isValidAnswer(STATUS_SUCCESS): 1239 # We clean the stuff we used in case we want to authenticate again 1240 # within the same connection 1241 self._Session['UserCredentials'] = '' 1242 self._Session['Connection'] = 0 1243 self._Session['SessionID'] = 0 1244 self._Session['SigningRequired'] = False 1245 self._Session['SigningKey'] = '' 1246 self._Session['SessionKey'] = '' 1247 self._Session['SigningActivated'] = False 1248 return True 1249 1250 def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ): 1251 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1252 raise SessionError(STATUS_INVALID_PARAMETER) 1253 if self._Session['OpenTable'].has_key(fileId) is False: 1254 raise SessionError(STATUS_INVALID_PARAMETER) 1255 1256 packet = self.SMB_PACKET() 1257 packet['Command'] = SMB2_QUERY_INFO 1258 packet['TreeID'] = treeId 1259 1260 queryInfo = SMB2QueryInfo() 1261 queryInfo['FileID'] = fileId 1262 queryInfo['InfoType'] = SMB2_0_INFO_FILE 1263 queryInfo['FileInfoClass'] = fileInfoClass 1264 queryInfo['OutputBufferLength'] = 65535 1265 queryInfo['AdditionalInformation'] = additionalInformation 1266 if len(inputBlob) == 0: 1267 queryInfo['InputBufferOffset'] = 0 1268 queryInfo['Buffer'] = '\x00' 1269 else: 1270 queryInfo['InputBufferLength'] = len(inputBlob) 1271 queryInfo['Buffer'] = inputBlob 1272 queryInfo['Flags'] = flags 1273 1274 packet['Data'] = queryInfo 1275 packetID = self.sendSMB(packet) 1276 ans = self.recvSMB(packetID) 1277 1278 if ans.isValidAnswer(STATUS_SUCCESS): 1279 queryResponse = SMB2QueryInfo_Response(ans['Data']) 1280 return queryResponse['Buffer'] 1281 1282 def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ): 1283 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1284 raise SessionError(STATUS_INVALID_PARAMETER) 1285 if self._Session['OpenTable'].has_key(fileId) is False: 1286 raise SessionError(STATUS_INVALID_PARAMETER) 1287 1288 packet = self.SMB_PACKET() 1289 packet['Command'] = SMB2_SET_INFO 1290 packet['TreeID'] = treeId 1291 1292 setInfo = SMB2SetInfo() 1293 setInfo['InfoType'] = SMB2_0_INFO_FILE 1294 setInfo['FileInfoClass'] = fileInfoClass 1295 setInfo['BufferLength'] = len(inputBlob) 1296 setInfo['AdditionalInformation'] = additionalInformation 1297 setInfo['FileID'] = fileId 1298 setInfo['Buffer'] = inputBlob 1299 1300 packet['Data'] = setInfo 1301 packetID = self.sendSMB(packet) 1302 ans = self.recvSMB(packetID) 1303 1304 if ans.isValidAnswer(STATUS_SUCCESS): 1305 return True 1306 1307 def getSessionKey(self): 1308 if self.getDialect() == SMB2_DIALECT_30: 1309 return self._Session['ApplicationKey'] 1310 else: 1311 return self._Session['SessionKey'] 1312 1313 def setSessionKey(self, key): 1314 if self.getDialect() == SMB2_DIALECT_30: 1315 self._Session['ApplicationKey'] = key 1316 else: 1317 self._Session['SessionKey'] = key 1318 1319 ###################################################################### 1320 # Higher level functions 1321 1322 def rename(self, shareName, oldPath, newPath): 1323 oldPath = string.replace(oldPath,'/', '\\') 1324 oldPath = ntpath.normpath(oldPath) 1325 if len(oldPath) > 0 and oldPath[0] == '\\': 1326 oldPath = oldPath[1:] 1327 1328 newPath = string.replace(newPath,'/', '\\') 1329 newPath = ntpath.normpath(newPath) 1330 if len(newPath) > 0 and newPath[0] == '\\': 1331 newPath = newPath[1:] 1332 1333 treeId = self.connectTree(shareName) 1334 fileId = None 1335 try: 1336 fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0) 1337 renameReq = FILE_RENAME_INFORMATION_TYPE_2() 1338 renameReq['ReplaceIfExists'] = 1 1339 renameReq['RootDirectory'] = '\x00'*8 1340 renameReq['FileNameLength'] = len(newPath)*2 1341 renameReq['FileName'] = newPath.encode('utf-16le') 1342 self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO) 1343 finally: 1344 if fileId is not None: 1345 self.close(treeId, fileId) 1346 self.disconnectTree(treeId) 1347 1348 return True 1349 1350 def writeFile(self, treeId, fileId, data, offset = 0): 1351 finished = False 1352 writeOffset = offset 1353 while not finished: 1354 if len(data) == 0: 1355 break 1356 writeData = data[:self._Connection['MaxWriteSize']] 1357 data = data[self._Connection['MaxWriteSize']:] 1358 written = self.write(treeId, fileId, writeData, writeOffset, len(writeData)) 1359 writeOffset += written 1360 return writeOffset - offset 1361 1362 def listPath(self, shareName, path, password = None): 1363 # ToDo: Handle situations where share is password protected 1364 path = string.replace(path,'/', '\\') 1365 path = ntpath.normpath(path) 1366 if len(path) > 0 and path[0] == '\\': 1367 path = path[1:] 1368 1369 treeId = self.connectTree(shareName) 1370 1371 fileId = None 1372 try: 1373 # ToDo, we're assuming it's a directory, we should check what the file type is 1374 fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0) 1375 res = '' 1376 files = [] 1377 from impacket import smb 1378 while True: 1379 try: 1380 res = self.queryDirectory( treeId, fileId, ntpath.basename(path), maxBufferSize = 65535, informationClass = FILE_FULL_DIRECTORY_INFORMATION ) 1381 nextOffset = 1 1382 while nextOffset != 0: 1383 fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE) 1384 fileInfo.fromString(res) 1385 files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le'))) 1386 nextOffset = fileInfo['NextEntryOffset'] 1387 res = res[nextOffset:] 1388 except SessionError, e: 1389 if (e.get_error_code()) != STATUS_NO_MORE_FILES: 1390 raise 1391 break 1392 finally: 1393 if fileId is not None: 1394 self.close(treeId, fileId) 1395 self.disconnectTree(treeId) 1396 1397 return files 1398 1399 def mkdir(self, shareName, pathName, password = None): 1400 # ToDo: Handle situations where share is password protected 1401 pathName = string.replace(pathName,'/', '\\') 1402 pathName = ntpath.normpath(pathName) 1403 if len(pathName) > 0 and pathName[0] == '\\': 1404 pathName = pathName[1:] 1405 1406 treeId = self.connectTree(shareName) 1407 1408 fileId = None 1409 try: 1410 fileId = self.create(treeId, pathName,GENERIC_ALL ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0) 1411 finally: 1412 if fileId is not None: 1413 self.close(treeId, fileId) 1414 self.disconnectTree(treeId) 1415 1416 return True 1417 1418 def rmdir(self, shareName, pathName, password = None): 1419 # ToDo: Handle situations where share is password protected 1420 pathName = string.replace(pathName,'/', '\\') 1421 pathName = ntpath.normpath(pathName) 1422 if len(pathName) > 0 and pathName[0] == '\\': 1423 pathName = pathName[1:] 1424 1425 treeId = self.connectTree(shareName) 1426 1427 fileId = None 1428 try: 1429 fileId = self.create(treeId, pathName, DELETE, FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0) 1430 finally: 1431 if fileId is not None: 1432 self.close(treeId, fileId) 1433 self.disconnectTree(treeId) 1434 1435 return True 1436 1437 def remove(self, shareName, pathName, password = None): 1438 # ToDo: Handle situations where share is password protected 1439 pathName = string.replace(pathName,'/', '\\') 1440 pathName = ntpath.normpath(pathName) 1441 if len(pathName) > 0 and pathName[0] == '\\': 1442 pathName = pathName[1:] 1443 1444 treeId = self.connectTree(shareName) 1445 1446 fileId = None 1447 try: 1448 fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0) 1449 finally: 1450 if fileId is not None: 1451 self.close(treeId, fileId) 1452 self.disconnectTree(treeId) 1453 1454 return True 1455 1456 def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ): 1457 # ToDo: Handle situations where share is password protected 1458 path = string.replace(path,'/', '\\') 1459 path = ntpath.normpath(path) 1460 if len(path) > 0 and path[0] == '\\': 1461 path = path[1:] 1462 1463 treeId = self.connectTree(shareName) 1464 fileId = None 1465 from impacket import smb 1466 try: 1467 fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0) 1468 res = self.queryInfo(treeId, fileId) 1469 fileInfo = smb.SMBQueryFileStandardInfo(res) 1470 fileSize = fileInfo['EndOfFile'] 1471 if (fileSize-offset) < self._Connection['MaxReadSize']: 1472 # Skip reading 0 bytes files. 1473 if (fileSize-offset) > 0: 1474 data = self.read(treeId, fileId, offset, fileSize-offset) 1475 callback(data) 1476 else: 1477 written = 0 1478 toBeRead = fileSize-offset 1479 while written < toBeRead: 1480 data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize']) 1481 written += len(data) 1482 offset += len(data) 1483 callback(data) 1484 finally: 1485 if fileId is not None: 1486 self.close(treeId, fileId) 1487 self.disconnectTree(treeId) 1488 1489 def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE): 1490 # ToDo: Handle situations where share is password protected 1491 path = string.replace(path,'/', '\\') 1492 path = ntpath.normpath(path) 1493 if len(path) > 0 and path[0] == '\\': 1494 path = path[1:] 1495 1496 treeId = self.connectTree(shareName) 1497 fileId = None 1498 try: 1499 fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0) 1500 finished = False 1501 writeOffset = offset 1502 while not finished: 1503 data = callback(self._Connection['MaxWriteSize']) 1504 if len(data) == 0: 1505 break 1506 written = self.write(treeId, fileId, data, writeOffset, len(data)) 1507 writeOffset += written 1508 finally: 1509 if fileId is not None: 1510 self.close(treeId, fileId) 1511 self.disconnectTree(treeId) 1512 1513 def waitNamedPipe(self, treeId, pipename, timeout = 5): 1514 pipename = ntpath.basename(pipename) 1515 if self._Session['TreeConnectTable'].has_key(treeId) is False: 1516 raise SessionError(STATUS_INVALID_PARAMETER) 1517 if len(pipename) > 0xffff: 1518 raise SessionError(STATUS_INVALID_PARAMETER) 1519 1520 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE() 1521 pipeWait['Timeout'] = timeout*100000 1522 pipeWait['NameLength'] = len(pipename)*2 1523 pipeWait['TimeoutSpecified'] = 1 1524 pipeWait['Name'] = pipename.encode('utf-16le') 1525 1526 return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0) 1527 1528 def getIOCapabilities(self): 1529 res = dict() 1530 1531 res['MaxReadSize'] = self._Connection['MaxReadSize'] 1532 res['MaxWriteSize'] = self._Connection['MaxWriteSize'] 1533 return res 1534 1535 1536 ###################################################################### 1537 # Backward compatibility functions and alias for SMB1 and DCE Transports 1538 # NOTE: It is strongly recommended not to use these commands 1539 # when implementing new client calls. 1540 get_server_name = getServerName 1541 get_server_domain = getServerDomain 1542 get_server_dns_domain_name = getServerDNSDomainName 1543 get_remote_name = getServerName 1544 get_remote_host = getServerIP 1545 get_server_os = getServerOS 1546 get_server_os_major = getServerOSMajor 1547 get_server_os_minor = getServerOSMinor 1548 get_server_os_build = getServerOSBuild 1549 tree_connect_andx = connectTree 1550 tree_connect = connectTree 1551 connect_tree = connectTree 1552 disconnect_tree = disconnectTree 1553 set_timeout = setTimeout 1554 use_timeout = useTimeout 1555 stor_file = storeFile 1556 retr_file = retrieveFile 1557 list_path = listPath 1558 1559 def __del__(self): 1560 if self._NetBIOSSession: 1561 self._NetBIOSSession.close() 1562 1563 1564 def doesSupportNTLMv2(self): 1565 # Always true :P 1566 return True 1567 1568 def is_login_required(self): 1569 # Always true :P 1570 return True 1571 1572 def is_signing_required(self): 1573 return self._Session["SigningRequired"] 1574 1575 def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None): 1576 if len(fileName) > 0 and fileName[0] == '\\': 1577 fileName = fileName[1:] 1578 1579 if cmd is not None: 1580 from impacket import smb 1581 ntCreate = smb.SMBCommand(data = str(cmd)) 1582 params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters']) 1583 return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'], 1584 params['CreateOptions'], params['Disposition'], params['FileAttributes'], 1585 params['Impersonation'], params['SecurityFlags']) 1586 1587 else: 1588 return self.create(treeId, fileName, 1589 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | 1590 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL, 1591 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 ) 1592 1593 def get_socket(self): 1594 return self._NetBIOSSession.get_socket() 1595 1596 1597 def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None): 1598 # ToDo: Handle the custom smb_packet situation 1599 return self.write(tid, fid, data, offset, len(data)) 1600 1601 def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0): 1602 return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer) 1603 1604 def TransactNamedPipeRecv(self): 1605 ans = self.recvSMB() 1606 1607 if ans.isValidAnswer(STATUS_SUCCESS): 1608 smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 1609 return smbIoctlResponse['Buffer'] 1610 1611 1612 def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None): 1613 # ToDo: Handle the custom smb_packet situation 1614 if max_size is None: 1615 max_size = self._Connection['MaxReadSize'] 1616 return self.read(tid, fid, offset, max_size, wait_answer) 1617 1618 def list_shared(self): 1619 # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED 1620 raise SessionError(STATUS_NOT_IMPLEMENTED) 1621 1622 def open_andx(self, tid, fileName, open_mode, desired_access): 1623 # ToDo Return all the attributes of the file 1624 if len(fileName) > 0 and fileName[0] == '\\': 1625 fileName = fileName[1:] 1626 1627 fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0) 1628 return fileId, 0, 0, 0, 0, 0, 0, 0, 0 1629 1630