Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

2# 

3# This software is provided under under a slightly modified version 

4# of the Apache Software License. See the accompanying LICENSE file 

5# for more information. 

6# 

7# Author: Alberto Solino (@agsolino) 

8# 

9# Description: 

10# [MS-SMB2] Protocol Implementation (SMB2 and SMB3) 

11# As you might see in the code, it's implemented strictly following 

12# the structures defined in the protocol specification. This may 

13# not be the most efficient way (e.g. self._Connection is the 

14# same to self._Session in the context of this library ) but 

15# it certainly helps following the document way easier. 

16# 

17# ToDo: 

18# [X] Implement SMB2_CHANGE_NOTIFY 

19# [X] Implement SMB2_QUERY_INFO 

20# [X] Implement SMB2_SET_INFO 

21# [ ] Implement SMB2_OPLOCK_BREAK 

22# [X] Implement SMB3 signing 

23# [X] Implement SMB3 encryption 

24# [ ] Add more backward compatible commands from the smb.py code 

25# [ ] Fix up all the 'ToDo' comments inside the code 

26# 

27from __future__ import division 

28from __future__ import print_function 

29 

30import socket 

31import ntpath 

32import random 

33import string 

34import struct 

35from six import indexbytes, b 

36from binascii import a2b_hex 

37from contextlib import contextmanager 

38from pyasn1.type.univ import noValue 

39from Cryptodome.Cipher import AES 

40 

41from impacket import nmb, ntlm, uuid, crypto 

42from impacket.smb3structs import * 

43from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \ 

44 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES 

45from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID 

46from impacket.krb5.gssapi import KRB5_AP_REQ 

47 

48 

49# For signing 

50import hashlib, hmac, copy 

51 

52# Our random number generator 

53try: 

54 rand = random.SystemRandom() 

55except NotImplementedError: 

56 rand = random 

57 pass 

58 

59# Structs to be used 

60TREE_CONNECT = { 

61 'ShareName' : '', 

62 'TreeConnectId' : 0, 

63 'Session' : 0, 

64 'IsDfsShare' : False, 

65 # If the client implements the SMB 3.0 dialect, 

66 # the client MUST also implement the following 

67 'IsCAShare' : False, 

68 'EncryptData' : False, 

69 'IsScaleoutShare' : False, 

70 # Outside the protocol 

71 'NumberOfUses' : 0, 

72} 

73 

74FILE = { 

75 'OpenTable' : [], 

76 'LeaseKey' : '', 

77 'LeaseState' : 0, 

78 'LeaseEpoch' : 0, 

79} 

80 

81OPEN = { 

82 'FileID' : '', 

83 'TreeConnect' : 0, 

84 'Connection' : 0, # Not Used 

85 'Oplocklevel' : 0, 

86 'Durable' : False, 

87 'FileName' : '', 

88 'ResilientHandle' : False, 

89 'LastDisconnectTime' : 0, 

90 'ResilientTimeout' : 0, 

91 'OperationBuckets' : [], 

92 # If the client implements the SMB 3.0 dialect, 

93 # the client MUST implement the following 

94 'CreateGuid' : '', 

95 'IsPersistent' : False, 

96 'DesiredAccess' : '', 

97 'ShareMode' : 0, 

98 'CreateOption' : '', 

99 'FileAttributes' : '', 

100 'CreateDisposition' : '', 

101} 

102 

103REQUEST = { 

104 'CancelID' : '', 

105 'Message' : '', 

106 'Timestamp' : 0, 

107} 

108 

109CHANNEL = { 

110 'SigningKey' : '', 

111 'Connection' : 0, 

112} 

113 

114 

115class SessionError(Exception): 

116 def __init__( self, error = 0, packet=0): 

117 Exception.__init__(self) 

118 self.error = error 

119 self.packet = packet 

120 

121 def get_error_code( self ): 

122 return self.error 

123 

124 def get_error_packet( self ): 

125 return self.packet 

126 

127 def __str__( self ): 

128 return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error]) 

129 

130 

131class SMB3: 

132 class HostnameValidationException(Exception): 

133 pass 

134 

135 def __init__(self, remote_name, remote_host, my_name=None, host_type=nmb.TYPE_SERVER, sess_port=445, timeout=60, 

136 UDP=0, preferredDialect=None, session=None, negSessionResponse=None): 

137 

138 # [MS-SMB2] Section 3 

139 self.RequireMessageSigning = False # 

140 self.ConnectionTable = {} 

141 self.GlobalFileTable = {} 

142 self.ClientGuid = ''.join([random.choice(string.ascii_letters) for i in range(16)]) 

143 # Only for SMB 3.0 

144 self.EncryptionAlgorithmList = ['AES-CCM'] 

145 self.MaxDialect = [] 

146 self.RequireSecureNegotiate = False 

147 

148 # Per Transport Connection Data 

149 self._Connection = { 

150 # Indexed by SessionID 

151 #'SessionTable' : {}, 

152 # Indexed by MessageID 

153 'OutstandingRequests' : {}, 

154 'OutstandingResponses' : {}, # 

155 'SequenceWindow' : 0, # 

156 'GSSNegotiateToken' : '', # 

157 'MaxTransactSize' : 0, # 

158 'MaxReadSize' : 0, # 

159 'MaxWriteSize' : 0, # 

160 'ServerGuid' : '', # 

161 'RequireSigning' : False, # 

162 'ServerName' : '', # 

163 # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST 

164 # also implement the following 

165 'Dialect' : 0, # 

166 'SupportsFileLeasing' : False, # 

167 'SupportsMultiCredit' : False, # 

168 # If the client implements the SMB 3.0 dialect, 

169 # it MUST also implement the following 

170 'SupportsDirectoryLeasing' : False, # 

171 'SupportsMultiChannel' : False, # 

172 'SupportsPersistentHandles': False, # 

173 'SupportsEncryption' : False, # 

174 'ClientCapabilities' : 0, 

175 'ServerCapabilities' : 0, # 

176 'ClientSecurityMode' : 0, # 

177 'ServerSecurityMode' : 0, # 

178 # Outside the protocol 

179 'ServerIP' : '', # 

180 'ClientName' : '', # 

181 #GSSoptions (MutualAuth and Delegate) 

182 'GSSoptions' : {}, 

183 'PreauthIntegrityHashValue': a2b_hex(b'0'*128) 

184 } 

185 

186 self._Session = { 

187 'SessionID' : 0, # 

188 'TreeConnectTable' : {}, # 

189 'SessionKey' : b'', # 

190 'SigningRequired' : False, # 

191 'Connection' : 0, # 

192 'UserCredentials' : '', # 

193 'OpenTable' : {}, # 

194 # If the client implements the SMB 3.0 dialect, 

195 # it MUST also implement the following 

196 'ChannelList' : [], 

197 'ChannelSequence' : 0, 

198 #'EncryptData' : False, 

199 'EncryptData' : True, 

200 'EncryptionKey' : '', 

201 'DecryptionKey' : '', 

202 'SigningKey' : '', 

203 'ApplicationKey' : b'', 

204 # Outside the protocol 

205 'SessionFlags' : 0, # 

206 'ServerName' : '', # 

207 'ServerDomain' : '', # 

208 'ServerDNSDomainName' : '', # 

209 'ServerDNSHostName' : '', # 

210 'ServerOS' : '', # 

211 'SigningActivated' : False, # 

212 'PreauthIntegrityHashValue': a2b_hex(b'0'*128), 

213 'CalculatePreAuthHash' : True, 

214 } 

215 

216 self.SMB_PACKET = SMB2Packet 

217 

218 self._timeout = timeout 

219 self._Connection['ServerIP'] = remote_host 

220 self._NetBIOSSession = None 

221 self._preferredDialect = preferredDialect 

222 self._doKerberos = False 

223 

224 # Strict host validation - off by default 

225 self._strict_hostname_validation = False 

226 self._validation_allow_absent = True 

227 self._accepted_hostname = '' 

228 

229 self.__userName = '' 

230 self.__password = '' 

231 self.__domain = '' 

232 self.__lmhash = '' 

233 self.__nthash = '' 

234 self.__kdc = '' 

235 self.__aesKey = '' 

236 self.__TGT = None 

237 self.__TGS = None 

238 

239 if sess_port == 445 and remote_name == '*SMBSERVER': 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true

240 self._Connection['ServerName'] = remote_host 

241 else: 

242 self._Connection['ServerName'] = remote_name 

243 

244 # This is on purpose. I'm still not convinced to do a socket.gethostname() if not specified 

245 if my_name is None: 245 ↛ 248line 245 didn't jump to line 248, because the condition on line 245 was never false

246 self._Connection['ClientName'] = '' 

247 else: 

248 self._Connection['ClientName'] = my_name 

249 

250 if session is None: 

251 if not my_name: 251 ↛ 258line 251 didn't jump to line 258, because the condition on line 251 was never false

252 # If destination port is 139 yes, there's some client disclosure 

253 my_name = socket.gethostname() 

254 i = my_name.find('.') 

255 if i > -1: 255 ↛ 256line 255 didn't jump to line 256, because the condition on line 255 was never true

256 my_name = my_name[:i] 

257 

258 if UDP: 258 ↛ 259line 258 didn't jump to line 259, because the condition on line 258 was never true

259 self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 

260 else: 

261 self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout) 

262 

263 self.negotiateSession(preferredDialect) 

264 else: 

265 self._NetBIOSSession = session 

266 # We should increase the SequenceWindow since a packet was already received. 

267 self._Connection['SequenceWindow'] += 1 

268 # Let's negotiate again if needed (or parse the existing response) using the same connection 

269 self.negotiateSession(preferredDialect, negSessionResponse) 

270 

271 def printStatus(self): 

272 print("CONNECTION") 

273 for i in list(self._Connection.items()): 

274 print("%-40s : %s" % i) 

275 print() 

276 print("SESSION") 

277 for i in list(self._Session.items()): 

278 print("%-40s : %s" % i) 

279 

280 def __UpdateConnectionPreAuthHash(self, data): 

281 from Cryptodome.Hash import SHA512 

282 calculatedHash = SHA512.new() 

283 calculatedHash.update(self._Connection['PreauthIntegrityHashValue']) 

284 calculatedHash.update(data) 

285 self._Connection['PreauthIntegrityHashValue'] = calculatedHash.digest() 

286 

287 def __UpdatePreAuthHash(self, data): 

288 from Cryptodome.Hash import SHA512 

289 calculatedHash = SHA512.new() 

290 calculatedHash.update(self._Session['PreauthIntegrityHashValue']) 

291 calculatedHash.update(data) 

292 self._Session['PreauthIntegrityHashValue'] = calculatedHash.digest() 

293 

294 def getKerberos(self): 

295 return self._doKerberos 

296 

297 def getServerName(self): 

298 return self._Session['ServerName'] 

299 

300 def getClientName(self): 

301 return self._Session['ClientName'] 

302 

303 def getRemoteName(self): 

304 if self._Session['ServerName'] == '': 304 ↛ 305line 304 didn't jump to line 305, because the condition on line 304 was never true

305 return self._Connection['ServerName'] 

306 return self._Session['ServerName'] 

307 

308 def setRemoteName(self, name): 

309 self._Session['ServerName'] = name 

310 return True 

311 

312 def getServerIP(self): 

313 return self._Connection['ServerIP'] 

314 

315 def getServerDomain(self): 

316 return self._Session['ServerDomain'] 

317 

318 def getServerDNSDomainName(self): 

319 return self._Session['ServerDNSDomainName'] 

320 

321 def getServerDNSHostName(self): 

322 return self._Session['ServerDNSHostName'] 

323 

324 def getServerOS(self): 

325 return self._Session['ServerOS'] 

326 

327 def getServerOSMajor(self): 

328 return self._Session['ServerOSMajor'] 

329 

330 def getServerOSMinor(self): 

331 return self._Session['ServerOSMinor'] 

332 

333 def getServerOSBuild(self): 

334 return self._Session['ServerOSBuild'] 

335 

336 def isGuestSession(self): 

337 return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST 

338 

339 def setTimeout(self, timeout): 

340 self._timeout = timeout 

341 

342 @contextmanager 

343 def useTimeout(self, timeout): 

344 prev_timeout = self.getTimeout(timeout) 

345 try: 

346 yield 

347 finally: 

348 self.setTimeout(prev_timeout) 

349 

350 def getDialect(self): 

351 return self._Connection['Dialect'] 

352 

353 def signSMB(self, packet): 

354 packet['Signature'] = '\x00'*16 

355 if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002: 

356 if len(self._Session['SessionKey']) > 0: 356 ↛ exitline 356 didn't return from function 'signSMB', because the condition on line 356 was never false

357 signature = hmac.new(self._Session['SessionKey'], packet.getData(), hashlib.sha256).digest() 

358 packet['Signature'] = signature[:16] 

359 else: 

360 if len(self._Session['SessionKey']) > 0: 360 ↛ exitline 360 didn't return from function 'signSMB', because the condition on line 360 was never false

361 p = packet.getData() 

362 signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p)) 

363 packet['Signature'] = signature 

364 

365 def sendSMB(self, packet): 

366 # The idea here is to receive multiple/single commands and create a compound request, and send it 

367 # Should return the MessageID for later retrieval. Implement compounded related requests. 

368 

369 # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or 

370 # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the 

371 # SMB2 header to Session.ChannelSequence 

372 

373 # Check this is not a CANCEL request. If so, don't consume sequence numbers 

374 if packet['Command'] is not SMB2_CANCEL: 374 ↛ 377line 374 didn't jump to line 377, because the condition on line 374 was never false

375 packet['MessageID'] = self._Connection['SequenceWindow'] 

376 self._Connection['SequenceWindow'] += 1 

377 packet['SessionID'] = self._Session['SessionID'] 

378 

379 # Default the credit charge to 1 unless set by the caller 

380 if ('CreditCharge' in packet.fields) is False: 

381 packet['CreditCharge'] = 1 

382 

383 # Standard credit request after negotiating protocol 

384 if self._Connection['SequenceWindow'] > 3: 

385 packet['CreditRequestResponse'] = 127 

386 

387 messageId = packet['MessageID'] 

388 

389 if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2: 

390 if packet['TreeID'] > 0 and (packet['TreeID'] in self._Session['TreeConnectTable']) is True: 

391 if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False: 391 ↛ 398line 391 didn't jump to line 398, because the condition on line 391 was never false

392 packet['Flags'] = SMB2_FLAGS_SIGNED 

393 self.signSMB(packet) 

394 elif packet['TreeID'] == 0: 394 ↛ 398line 394 didn't jump to line 398, because the condition on line 394 was never false

395 packet['Flags'] = SMB2_FLAGS_SIGNED 

396 self.signSMB(packet) 

397 

398 if packet['Command'] is SMB2_NEGOTIATE: 

399 data = packet.getData() 

400 self.__UpdateConnectionPreAuthHash(data) 

401 self._Session['CalculatePreAuthHash'] = False 

402 

403 if packet['Command'] is SMB2_SESSION_SETUP: 

404 self._Session['CalculatePreAuthHash'] = True 

405 

406 if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True): 

407 plainText = packet.getData() 

408 transformHeader = SMB2_TRANSFORM_HEADER() 

409 transformHeader['Nonce'] = ''.join([rand.choice(string.ascii_letters) for _ in range(11)]) 

410 transformHeader['OriginalMessageSize'] = len(plainText) 

411 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM 

412 transformHeader['SessionID'] = self._Session['SessionID'] 

413 cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, b(transformHeader['Nonce'])) 

414 cipher.update(transformHeader.getData()[20:]) 

415 cipherText = cipher.encrypt(plainText) 

416 transformHeader['Signature'] = cipher.digest() 

417 packet = transformHeader.getData() + cipherText 

418 

419 self._NetBIOSSession.send_packet(packet) 

420 else: 

421 data = packet.getData() 

422 if self._Session['CalculatePreAuthHash'] is True: 

423 self.__UpdatePreAuthHash(data) 

424 

425 self._NetBIOSSession.send_packet(data) 

426 

427 return messageId 

428 

429 def recvSMB(self, packetID = None): 

430 # First, verify we don't have the packet already 

431 if packetID in self._Connection['OutstandingResponses']: 431 ↛ 432line 431 didn't jump to line 432, because the condition on line 431 was never true

432 return self._Connection['OutstandingResponses'].pop(packetID) 

433 

434 data = self._NetBIOSSession.recv_packet(self._timeout) 

435 

436 if data.get_trailer().startswith(b'\xfdSMB'): 

437 # Packet is encrypted 

438 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

439 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 

440 cipher.update(transformHeader.getData()[20:]) 

441 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 

442 #cipher.verify(transformHeader['Signature']) 

443 packet = SMB2Packet(plainText) 

444 else: 

445 # In all SMB dialects for a response this field is interpreted as the Status field. 

446 # This field can be set to any value. For a list of valid status codes, 

447 # see [MS-ERREF] section 2.3. 

448 packet = SMB2Packet(data.get_trailer()) 

449 

450 # Loop while we receive pending requests 

451 if packet['Status'] == STATUS_PENDING: 

452 status = STATUS_PENDING 

453 while status == STATUS_PENDING: 

454 data = self._NetBIOSSession.recv_packet(self._timeout) 

455 if data.get_trailer().startswith(b'\xfeSMB'): 455 ↛ 456line 455 didn't jump to line 456, because the condition on line 455 was never true

456 packet = SMB2Packet(data.get_trailer()) 

457 else: 

458 # Packet is encrypted 

459 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

460 cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11]) 

461 cipher.update(transformHeader.getData()[20:]) 

462 plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):]) 

463 #cipher.verify(transformHeader['Signature']) 

464 packet = SMB2Packet(plainText) 

465 status = packet['Status'] 

466 

467 if packet['MessageID'] == packetID or packetID is None: 467 ↛ 476line 467 didn't jump to line 476, because the condition on line 467 was never false

468 # Let's update the sequenceWindow based on the CreditsCharged 

469 # In the SMB 2.0.2 dialect, this field MUST NOT be used and MUST be reserved. 

470 # The sender MUST set this to 0, and the receiver MUST ignore it. 

471 # In all other dialects, this field indicates the number of credits that this request consumes. 

472 if self._Connection['Dialect'] > SMB2_DIALECT_002: 

473 self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1) 

474 return packet 

475 else: 

476 self._Connection['OutstandingResponses'][packet['MessageID']] = packet 

477 return self.recvSMB(packetID) 

478 

479 def negotiateSession(self, preferredDialect = None, negSessionResponse = None): 

480 # Let's store some data for later use 

481 self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

482 if self.RequireMessageSigning is True: 482 ↛ 483line 482 didn't jump to line 483, because the condition on line 482 was never true

483 self._Connection['ClientSecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED 

484 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION 

485 currentDialect = SMB2_DIALECT_WILDCARD 

486 

487 # Do we have a negSessionPacket already? 

488 if negSessionResponse is not None: 

489 # Yes, let's store the dialect answered back 

490 negResp = SMB2Negotiate_Response(negSessionResponse['Data']) 

491 currentDialect = negResp['DialectRevision'] 

492 

493 if currentDialect == SMB2_DIALECT_WILDCARD: 493 ↛ 563line 493 didn't jump to line 563, because the condition on line 493 was never false

494 # Still don't know the chosen dialect, let's send our options 

495 

496 packet = self.SMB_PACKET() 

497 packet['Command'] = SMB2_NEGOTIATE 

498 negSession = SMB2Negotiate() 

499 

500 negSession['SecurityMode'] = self._Connection['ClientSecurityMode'] 

501 negSession['Capabilities'] = self._Connection['Capabilities'] 

502 negSession['ClientGuid'] = self.ClientGuid 

503 if preferredDialect is not None: 

504 negSession['Dialects'] = [preferredDialect] 

505 if preferredDialect == SMB2_DIALECT_311: 505 ↛ 507line 505 didn't jump to line 507, because the condition on line 505 was never true

506 # Build the Contexts 

507 contextData = SMB311ContextData() 

508 contextData['NegotiateContextOffset'] = 64+38+2 

509 contextData['NegotiateContextCount'] = 0 

510 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_PREAUTH_INTEGRITY_CAPABILITIES 

511 # to the negotiate request as specified in section 2.2.3.1: 

512 negotiateContext = SMB2NegotiateContext() 

513 negotiateContext['ContextType'] = SMB2_PREAUTH_INTEGRITY_CAPABILITIES 

514 

515 preAuthIntegrityCapabilities = SMB2PreAuthIntegrityCapabilities() 

516 preAuthIntegrityCapabilities['HashAlgorithmCount'] = 1 

517 preAuthIntegrityCapabilities['SaltLength'] = 32 

518 preAuthIntegrityCapabilities['HashAlgorithms'] = b'\x01\x00' 

519 preAuthIntegrityCapabilities['Salt'] = ''.join([rand.choice(string.ascii_letters) for _ in 

520 range(preAuthIntegrityCapabilities['SaltLength'])]) 

521 

522 negotiateContext['Data'] = preAuthIntegrityCapabilities.getData() 

523 negotiateContext['DataLength'] = len(negotiateContext['Data']) 

524 contextData['NegotiateContextCount'] += 1 

525 pad = b'\xFF' * ((8 - (negotiateContext['DataLength'] % 8)) % 8) 

526 

527 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_ENCRYPTION_CAPABILITIES 

528 # to the negotiate request as specified in section 2.2.3.1 and initialize 

529 # the Ciphers field with the ciphers supported by the client in the order of preference. 

530 

531 negotiateContext2 = SMB2NegotiateContext () 

532 negotiateContext2['ContextType'] = SMB2_ENCRYPTION_CAPABILITIES 

533 

534 encryptionCapabilities = SMB2EncryptionCapabilities() 

535 encryptionCapabilities['CipherCount'] = 1 

536 encryptionCapabilities['Ciphers'] = 1 

537 

538 negotiateContext2['Data'] = encryptionCapabilities.getData() 

539 negotiateContext2['DataLength'] = len(negotiateContext2['Data']) 

540 contextData['NegotiateContextCount'] += 1 

541 

542 negSession['ClientStartTime'] = contextData.getData() 

543 negSession['Padding'] = b'\xFF\xFF' 

544 # Subsequent negotiate contexts MUST appear at the first 8-byte aligned offset following the 

545 # previous negotiate context. 

546 negSession['NegotiateContextList'] = negotiateContext.getData() + pad + negotiateContext2.getData() 

547 

548 # Do you want to enforce encryption? Uncomment here: 

549 #self._Connection['SupportsEncryption'] = True 

550 

551 else: 

552 negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30] 

553 negSession['DialectCount'] = len(negSession['Dialects']) 

554 packet['Data'] = negSession 

555 

556 packetID = self.sendSMB(packet) 

557 ans = self.recvSMB(packetID) 

558 if ans.isValidAnswer(STATUS_SUCCESS): 558 ↛ 563line 558 didn't jump to line 563, because the condition on line 558 was never false

559 negResp = SMB2Negotiate_Response(ans['Data']) 

560 if negResp['DialectRevision'] == SMB2_DIALECT_311: 560 ↛ 561line 560 didn't jump to line 561, because the condition on line 560 was never true

561 self.__UpdateConnectionPreAuthHash(ans.rawData) 

562 

563 self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize']) 

564 self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize']) 

565 self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize']) 

566 self._Connection['ServerGuid'] = negResp['ServerGuid'] 

567 self._Connection['GSSNegotiateToken'] = negResp['Buffer'] 

568 self._Connection['Dialect'] = negResp['DialectRevision'] 

569 if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED or \ 569 ↛ 572line 569 didn't jump to line 572, because the condition on line 569 was never false

570 self._Connection['Dialect'] == SMB2_DIALECT_311: 

571 self._Connection['RequireSigning'] = True 

572 if self._Connection['Dialect'] == SMB2_DIALECT_311: 572 ↛ 574line 572 didn't jump to line 574, because the condition on line 572 was never true

573 # Always Sign 

574 self._Connection['RequireSigning'] = True 

575 

576 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING: 

577 self._Connection['SupportsFileLeasing'] = True 

578 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU: 

579 self._Connection['SupportsMultiCredit'] = True 

580 

581 if self._Connection['Dialect'] >= SMB2_DIALECT_30: 

582 # Switching to the right packet format 

583 self.SMB_PACKET = SMB3Packet 

584 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING: 584 ↛ 585line 584 didn't jump to line 585, because the condition on line 584 was never true

585 self._Connection['SupportsDirectoryLeasing'] = True 

586 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL: 586 ↛ 587line 586 didn't jump to line 587, because the condition on line 586 was never true

587 self._Connection['SupportsMultiChannel'] = True 

588 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES: 588 ↛ 589line 588 didn't jump to line 589, because the condition on line 588 was never true

589 self._Connection['SupportsPersistentHandles'] = True 

590 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION: 590 ↛ 593line 590 didn't jump to line 593, because the condition on line 590 was never false

591 self._Connection['SupportsEncryption'] = True 

592 

593 self._Connection['ServerCapabilities'] = negResp['Capabilities'] 

594 self._Connection['ServerSecurityMode'] = negResp['SecurityMode'] 

595 

596 def getCredentials(self): 

597 return ( 

598 self.__userName, 

599 self.__password, 

600 self.__domain, 

601 self.__lmhash, 

602 self.__nthash, 

603 self.__aesKey, 

604 self.__TGT, 

605 self.__TGS) 

606 

607 def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None, mutualAuth=False): 

608 # If TGT or TGS are specified, they are in the form of: 

609 # TGS['KDC_REP'] = the response from the server 

610 # TGS['cipher'] = the cipher used 

611 # TGS['sessionKey'] = the sessionKey 

612 # If we have hashes, normalize them 

613 if lmhash != '' or nthash != '': 

614 if len(lmhash) % 2: lmhash = '0%s' % lmhash 

615 if len(nthash) % 2: nthash = '0%s' % nthash 

616 try: # just in case they were converted already 

617 lmhash = a2b_hex(lmhash) 

618 nthash = a2b_hex(nthash) 

619 except: 

620 pass 

621 

622 self.__userName = user 

623 self.__password = password 

624 self.__domain = domain 

625 self.__lmhash = lmhash 

626 self.__nthash = nthash 

627 self.__kdc = kdcHost 

628 self.__aesKey = aesKey 

629 self.__TGT = TGT 

630 self.__TGS = TGS 

631 self._doKerberos= True 

632 

633 sessionSetup = SMB2SessionSetup() 

634 if self.RequireMessageSigning is True: 634 ↛ 635line 634 didn't jump to line 635, because the condition on line 634 was never true

635 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

636 else: 

637 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

638 

639 sessionSetup['Flags'] = 0 

640 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 

641 

642 # Importing down here so pyasn1 is not required if kerberos is not used. 

643 from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set 

644 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS 

645 from impacket.krb5 import constants 

646 from impacket.krb5.types import Principal, KerberosTime, Ticket 

647 from pyasn1.codec.der import decoder, encoder 

648 import datetime 

649 

650 # First of all, we need to get a TGT for the user 

651 userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 

652 if TGT is None: 652 ↛ 656line 652 didn't jump to line 656, because the condition on line 652 was never false

653 if TGS is None: 653 ↛ 676line 653 didn't jump to line 676, because the condition on line 653 was never false

654 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) 

655 else: 

656 tgt = TGT['KDC_REP'] 

657 cipher = TGT['cipher'] 

658 sessionKey = TGT['sessionKey'] 

659 

660 # Save the ticket 

661 # If you want, for debugging purposes 

662# from impacket.krb5.ccache import CCache 

663# ccache = CCache() 

664# try: 

665# if TGS is None: 

666# ccache.fromTGT(tgt, oldSessionKey, sessionKey) 

667# else: 

668# ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] ) 

669# ccache.saveFile('/tmp/ticket.bin') 

670# except Exception, e: 

671# print e 

672# pass 

673 

674 # Now that we have the TGT, we should ask for a TGS for cifs 

675 

676 if TGS is None: 676 ↛ 680line 676 didn't jump to line 680, because the condition on line 676 was never false

677 serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value) 

678 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) 

679 else: 

680 tgs = TGS['KDC_REP'] 

681 cipher = TGS['cipher'] 

682 sessionKey = TGS['sessionKey'] 

683 

684 # Let's build a NegTokenInit with a Kerberos REQ_AP 

685 

686 blob = SPNEGO_NegTokenInit() 

687 

688 # Kerberos 

689 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] 

690 

691 # Let's extract the ticket from the TGS 

692 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] 

693 ticket = Ticket() 

694 ticket.from_asn1(tgs['ticket']) 

695 

696 # Now let's build the AP_REQ 

697 apReq = AP_REQ() 

698 apReq['pvno'] = 5 

699 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) 

700 

701 #Handle mutual authentication 

702 opts = list() 

703 

704 if mutualAuth == True: 704 ↛ 705line 704 didn't jump to line 705, because the condition on line 704 was never true

705 from impacket.krb5.constants import APOptions 

706 opts.append(constants.APOptions.mutual_required.value) 

707 

708 apReq['ap-options'] = constants.encodeFlags(opts) 

709 seq_set(apReq,'ticket', ticket.to_asn1) 

710 

711 authenticator = Authenticator() 

712 authenticator['authenticator-vno'] = 5 

713 authenticator['crealm'] = domain 

714 seq_set(authenticator, 'cname', userName.components_to_asn1) 

715 now = datetime.datetime.utcnow() 

716 

717 authenticator['cusec'] = now.microsecond 

718 authenticator['ctime'] = KerberosTime.to_asn1(now) 

719 

720 encodedAuthenticator = encoder.encode(authenticator) 

721 

722 # Key Usage 11 

723 # AP-REQ Authenticator (includes application authenticator 

724 # subkey), encrypted with the application session key 

725 # (Section 5.5.1) 

726 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) 

727 

728 apReq['authenticator'] = noValue 

729 apReq['authenticator']['etype'] = cipher.enctype 

730 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator 

731 

732 blob['MechToken'] = struct.pack('B', ASN1_AID) + asn1encode( struct.pack('B', ASN1_OID) + asn1encode( 

733 TypesMech['KRB5 - Kerberos 5'] ) + KRB5_AP_REQ + encoder.encode(apReq)) 

734 

735 sessionSetup['SecurityBufferLength'] = len(blob) 

736 sessionSetup['Buffer'] = blob.getData() 

737 

738 packet = self.SMB_PACKET() 

739 packet['Command'] = SMB2_SESSION_SETUP 

740 packet['Data'] = sessionSetup 

741 

742 #Initiate session preauth hash 

743 self._Session['PreauthIntegrityHashValue'] = self._Connection['PreauthIntegrityHashValue'] 

744 

745 packetID = self.sendSMB(packet) 

746 ans = self.recvSMB(packetID) 

747 if ans.isValidAnswer(STATUS_SUCCESS): 747 ↛ 848line 747 didn't jump to line 848, because the condition on line 747 was never false

748 self._Session['SessionID'] = ans['SessionID'] 

749 self._Session['SigningRequired'] = self._Connection['RequireSigning'] 

750 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 

751 self._Session['Connection'] = self._NetBIOSSession.get_socket() 

752 

753 

754 if mutualAuth == True: 754 ↛ 756line 754 didn't jump to line 756, because the condition on line 754 was never true

755 #Lets get the session key in the AP_REP 

756 from impacket.krb5.asn1 import AP_REP, EncAPRepPart 

757 from impacket.krb5.crypto import Key, _enctype_table 

758 smbSessSetupResp = SMB2SessionSetup_Response(ans['Data']) 

759 

760 #in [KILE] 3.1.1.2: 

761 # The subkey in the EncAPRepPart of the KRB_AP_REP message is used as the session key when 

762 # MutualAuthentication is requested. (The KRB_AP_REP message and its fields are defined in [RFC4120] 

763 # section 5.5.2.) When DES and RC4 are used, the implementation is as described in [RFC1964]. With 

764 # DES and RC4, the subkey in the KRB_AP_REQ message can be used as the session key, as it is the 

765 # same as the subkey in KRB_AP_REP message; however when AES is used (see [RFC4121]), the 

766 # subkeys are different and the subkey in the KRB_AP_REP is used. (The KRB_AP_REQ message is 

767 # defined in [RFC4120] section 5.5.1). 

768 negTokenResp = SPNEGO_NegTokenResp(smbSessSetupResp['Buffer']) 

769 

770 #TODO: Parse ResponseToken as krb5Blob depending on the supported mech indicated in the negTokenResp 

771 ap_rep = decoder.decode(negTokenResp['ResponseToken'][16:], asn1Spec=AP_REP())[0] 

772 

773 if cipher.enctype != ap_rep['enc-part']['etype']: 

774 raise Exception('Unable to decrypt AP_REP: cipher does not match TGS session key') 

775 

776 # Key Usage 12 

777 # AP-REP encrypted part (includes application session 

778 # subkey), encrypted with the application session key 

779 # (Section 5.5.2) 

780 cipherText = ap_rep['enc-part']['cipher'] 

781 plainText = cipher.decrypt(sessionKey, 12, cipherText) 

782 

783 encAPRepPart = decoder.decode(plainText, asn1Spec = EncAPRepPart())[0] 

784 

785 apCipher = _enctype_table[int(encAPRepPart['subkey']['keytype'])]() 

786 apSessionKey = Key(apCipher.enctype, encAPRepPart['subkey']['keyvalue'].asOctets()) 

787 

788 sequenceNumber = int(encAPRepPart['seq-number']) 

789 self._Session['SessionKey'] = apSessionKey.contents 

790 

791 else: 

792 self._Session['SessionKey'] = sessionKey.contents[:16] 

793 

794 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] >= SMB2_DIALECT_30: 

795 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBSigningKey" as the label; 

796 # otherwise, the case - sensitive ASCII string "SMB2AESCMAC" as the label. 

797 # If Connection.Dialect is "3.1.1", Session.PreauthIntegrityHashValue as the context; otherwise, 

798 # the case-sensitive ASCII string "SmbSign" as context for the algorithm. 

799 if self._Connection['Dialect'] == SMB2_DIALECT_311: 799 ↛ 800line 799 didn't jump to line 800, because the condition on line 799 was never true

800 self._Session['SigningKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBSigningKey\x00", 

801 self._Session['PreauthIntegrityHashValue'], 128) 

802 else: 

803 self._Session['SigningKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCMAC\x00", 

804 b"SmbSign\x00", 128) 

805 

806 # Do not encrypt anonymous connections 

807 if user == '' or self.isGuestSession(): 807 ↛ 808line 807 didn't jump to line 808, because the condition on line 807 was never true

808 self._Connection['SupportsEncryption'] = False 

809 

810 if self._Session['SigningRequired'] is True: 810 ↛ 812line 810 didn't jump to line 812, because the condition on line 810 was never false

811 self._Session['SigningActivated'] = True 

812 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True: 

813 # Encryption available. Let's enforce it if we have AES CCM available 

814 self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA 

815 # Application Key 

816 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBAppKey" as the label; 

817 # otherwise, the case-sensitive ASCII string "SMB2APP" as the label. Session.PreauthIntegrityHashValue 

818 # as the context; otherwise, the case-sensitive ASCII string "SmbRpc" as context for the algorithm. 

819 # Encryption Key 

820 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBC2SCipherKey" as # the label; 

821 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue 

822 # as the context; otherwise, the case-sensitive ASCII string "ServerIn " as context for the algorithm 

823 # (note the blank space at the end) 

824 # Decryption Key 

825 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBS2CCipherKey" as the label; 

826 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue 

827 # as the context; otherwise, the case-sensitive ASCII string "ServerOut" as context for the algorithm. 

828 if self._Connection['Dialect'] == SMB2_DIALECT_311: 828 ↛ 829line 828 didn't jump to line 829, because the condition on line 828 was never true

829 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBAppKey\x00", 

830 self._Session['PreauthIntegrityHashValue'], 128) 

831 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBC2SCipherKey\x00", 

832 self._Session['PreauthIntegrityHashValue'], 128) 

833 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMBS2CCipherKey\x00", 

834 self._Session['PreauthIntegrityHashValue'], 128) 

835 else: 

836 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2APP\x00", 

837 b"SmbRpc\x00", 128) 

838 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCCM\x00", 

839 b"ServerIn \x00", 128) 

840 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (self._Session['SessionKey'], b"SMB2AESCCM\x00", 

841 b"ServerOut\x00", 128) 

842 

843 self._Session['CalculatePreAuthHash'] = False 

844 return True 

845 else: 

846 # We clean the stuff we used in case we want to authenticate again 

847 # within the same connection 

848 self._Session['UserCredentials'] = '' 

849 self._Session['Connection'] = 0 

850 self._Session['SessionID'] = 0 

851 self._Session['SigningRequired'] = False 

852 self._Session['SigningKey'] = '' 

853 self._Session['SessionKey'] = '' 

854 self._Session['SigningActivated'] = False 

855 self._Session['CalculatePreAuthHash'] = False 

856 self._Session['PreauthIntegrityHashValue'] = a2b_hex(b'0'*128) 

857 raise Exception('Unsuccessful Login') 

858 

859 

860 def login(self, user, password, domain = '', lmhash = '', nthash = ''): 

861 # If we have hashes, normalize them 

862 if lmhash != '' or nthash != '': 

863 if len(lmhash) % 2: lmhash = '0%s' % lmhash 

864 if len(nthash) % 2: nthash = '0%s' % nthash 

865 try: # just in case they were converted already 

866 lmhash = a2b_hex(lmhash) 

867 nthash = a2b_hex(nthash) 

868 except: 

869 pass 

870 

871 self.__userName = user 

872 self.__password = password 

873 self.__domain = domain 

874 self.__lmhash = lmhash 

875 self.__nthash = nthash 

876 self.__aesKey = '' 

877 self.__TGT = None 

878 self.__TGS = None 

879 

880 sessionSetup = SMB2SessionSetup() 

881 if self.RequireMessageSigning is True: 881 ↛ 882line 881 didn't jump to line 882, because the condition on line 881 was never true

882 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

883 else: 

884 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

885 

886 sessionSetup['Flags'] = 0 

887 #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS 

888 

889 # Let's build a NegTokenInit with the NTLMSSP 

890 # TODO: In the future we should be able to choose different providers 

891 

892 blob = SPNEGO_NegTokenInit() 

893 

894 # NTLMSSP 

895 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 

896 auth = ntlm.getNTLMSSPType1(self._Connection['ClientName'],domain, self._Connection['RequireSigning']) 

897 blob['MechToken'] = auth.getData() 

898 

899 sessionSetup['SecurityBufferLength'] = len(blob) 

900 sessionSetup['Buffer'] = blob.getData() 

901 

902 # ToDo: 

903 # If this authentication is for establishing an alternative channel for an existing Session, as specified 

904 # in section 3.2.4.1.7, the client MUST also set the following values: 

905 # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new 

906 # channel being established. 

907 # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field. 

908 # The PreviousSessionId field MUST be set to zero. 

909 

910 packet = self.SMB_PACKET() 

911 packet['Command'] = SMB2_SESSION_SETUP 

912 packet['Data'] = sessionSetup 

913 

914 packetID = self.sendSMB(packet) 

915 ans = self.recvSMB(packetID) 

916 if self._Connection['Dialect'] == SMB2_DIALECT_311: 916 ↛ 917line 916 didn't jump to line 917, because the condition on line 916 was never true

917 self.__UpdatePreAuthHash (ans.rawData) 

918 

919 if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED): 919 ↛ exitline 919 didn't return from function 'login', because the condition on line 919 was never false

920 self._Session['SessionID'] = ans['SessionID'] 

921 self._Session['SigningRequired'] = self._Connection['RequireSigning'] 

922 self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash) 

923 self._Session['Connection'] = self._NetBIOSSession.get_socket() 

924 sessionSetupResponse = SMB2SessionSetup_Response(ans['Data']) 

925 respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer']) 

926 

927 # Let's parse some data and keep it to ourselves in case it is asked 

928 ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken']) 

929 if ntlmChallenge['TargetInfoFields_len'] > 0: 929 ↛ 971line 929 didn't jump to line 971, because the condition on line 929 was never false

930 av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']]) 

931 if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: 931 ↛ 937line 931 didn't jump to line 937, because the condition on line 931 was never false

932 try: 

933 self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') 

934 except: 

935 # For some reason, we couldn't decode Unicode here.. silently discard the operation 

936 pass 

937 if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: 937 ↛ 944line 937 didn't jump to line 944, because the condition on line 937 was never false

938 try: 

939 if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): 939 ↛ 944line 939 didn't jump to line 944, because the condition on line 939 was never false

940 self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le') 

941 except: 

942 # For some reason, we couldn't decode Unicode here.. silently discard the operation 

943 pass 

944 if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: 944 ↛ 951line 944 didn't jump to line 951, because the condition on line 944 was never false

945 try: 

946 self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le') 

947 except: 

948 # For some reason, we couldn't decode Unicode here.. silently discard the operation 

949 pass 

950 

951 if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] is not None: 951 ↛ 958line 951 didn't jump to line 958, because the condition on line 951 was never false

952 try: 

953 self._Session['ServerDNSHostName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode('utf-16le') 

954 except: 

955 # For some reason, we couldn't decode Unicode here.. silently discard the operation 

956 pass 

957 

958 if self._strict_hostname_validation: 958 ↛ 959line 958 didn't jump to line 959, because the condition on line 958 was never true

959 self.perform_hostname_validation() 

960 

961 # Parse Version to know the target Operating system name. Not provided elsewhere anymore 

962 if 'Version' in ntlmChallenge.fields: 962 ↛ 971line 962 didn't jump to line 971, because the condition on line 962 was never false

963 version = ntlmChallenge['Version'] 

964 

965 if len(version) >= 4: 965 ↛ 971line 965 didn't jump to line 971, because the condition on line 965 was never false

966 self._Session['ServerOS'] = "Windows %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack('<H',version[2:4])[0]) 

967 self._Session["ServerOSMajor"] = indexbytes(version,0) 

968 self._Session["ServerOSMinor"] = indexbytes(version,1) 

969 self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0] 

970 

971 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash) 

972 

973 

974 

975 respToken2 = SPNEGO_NegTokenResp() 

976 respToken2['ResponseToken'] = type3.getData() 

977 

978 # Reusing the previous structure 

979 sessionSetup['SecurityBufferLength'] = len(respToken2) 

980 sessionSetup['Buffer'] = respToken2.getData() 

981 

982 packetID = self.sendSMB(packet) 

983 packet = self.recvSMB(packetID) 

984 

985 # Let's calculate Key Materials before moving on 

986 if exportedSessionKey is not None: 986 ↛ 1001line 986 didn't jump to line 1001, because the condition on line 986 was never false

987 self._Session['SessionKey'] = exportedSessionKey 

988 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] >= SMB2_DIALECT_30: 

989 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBSigningKey" as the label; 

990 # otherwise, the case - sensitive ASCII string "SMB2AESCMAC" as the label. 

991 # If Connection.Dialect is "3.1.1", Session.PreauthIntegrityHashValue as the context; otherwise, 

992 # the case-sensitive ASCII string "SmbSign" as context for the algorithm. 

993 if self._Connection['Dialect'] == SMB2_DIALECT_311: 993 ↛ 994line 993 didn't jump to line 994, because the condition on line 993 was never true

994 self._Session['SigningKey'] = crypto.KDF_CounterMode (exportedSessionKey, 

995 b"SMBSigningKey\x00", 

996 self._Session['PreauthIntegrityHashValue'], 

997 128) 

998 else: 

999 self._Session['SigningKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCMAC\x00", 

1000 b"SmbSign\x00", 128) 

1001 try: 

1002 if packet.isValidAnswer(STATUS_SUCCESS): 1002 ↛ exitline 1002 didn't return from function 'login', because the condition on line 1002 was never false

1003 sessionSetupResponse = SMB2SessionSetup_Response(packet['Data']) 

1004 self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags'] 

1005 self._Session['SessionID'] = packet['SessionID'] 

1006 

1007 # Do not encrypt anonymous connections 

1008 if user == '' or self.isGuestSession(): 

1009 self._Connection['SupportsEncryption'] = False 

1010 

1011 # Calculate the key derivations for dialect 3.0 

1012 if self._Session['SigningRequired'] is True: 1012 ↛ 1014line 1012 didn't jump to line 1014, because the condition on line 1012 was never false

1013 self._Session['SigningActivated'] = True 

1014 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsEncryption'] is True: 

1015 # SMB 3.0. Encryption available. Let's enforce it if we have AES CCM available 

1016 self._Session['SessionFlags'] |= SMB2_SESSION_FLAG_ENCRYPT_DATA 

1017 # Application Key 

1018 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBAppKey" as the label; 

1019 # otherwise, the case-sensitive ASCII string "SMB2APP" as the label. Session.PreauthIntegrityHashValue 

1020 # as the context; otherwise, the case-sensitive ASCII string "SmbRpc" as context for the algorithm. 

1021 # Encryption Key 

1022 # If Connection.Dialect is "3.1.1",the case-sensitive ASCII string "SMBC2SCipherKey" as # the label; 

1023 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue 

1024 # as the context; otherwise, the case-sensitive ASCII string "ServerIn " as context for the algorithm 

1025 # (note the blank space at the end) 

1026 # Decryption Key 

1027 # If Connection.Dialect is "3.1.1", the case-sensitive ASCII string "SMBS2CCipherKey" as the label; 

1028 # otherwise, the case-sensitive ASCII string "SMB2AESCCM" as the label. Session.PreauthIntegrityHashValue 

1029 # as the context; otherwise, the case-sensitive ASCII string "ServerOut" as context for the algorithm. 

1030 if self._Connection['Dialect'] == SMB2_DIALECT_311: 1030 ↛ 1031line 1030 didn't jump to line 1031, because the condition on line 1030 was never true

1031 self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMBAppKey\x00", 

1032 self._Session['PreauthIntegrityHashValue'], 128) 

1033 self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, b"SMBC2SCipherKey\x00", 

1034 self._Session['PreauthIntegrityHashValue'], 128) 

1035 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMBS2CCipherKey\x00", 

1036 self._Session['PreauthIntegrityHashValue'], 128) 

1037 

1038 else: 

1039 self._Session['ApplicationKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2APP\x00", 

1040 b"SmbRpc\x00", 128) 

1041 self._Session['EncryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCCM\x00", 

1042 b"ServerIn \x00", 128) 

1043 self._Session['DecryptionKey'] = crypto.KDF_CounterMode (exportedSessionKey, b"SMB2AESCCM\x00", 

1044 b"ServerOut\x00", 128) 

1045 self._Session['CalculatePreAuthHash'] = False 

1046 return True 

1047 except: 

1048 # We clean the stuff we used in case we want to authenticate again 

1049 # within the same connection 

1050 self._Session['UserCredentials'] = '' 

1051 self._Session['Connection'] = 0 

1052 self._Session['SessionID'] = 0 

1053 self._Session['SigningRequired'] = False 

1054 self._Session['SigningKey'] = '' 

1055 self._Session['SessionKey'] = '' 

1056 self._Session['SigningActivated'] = False 

1057 self._Session['CalculatePreAuthHash'] = False 

1058 self._Session['PreauthIntegrityHashValue'] = a2b_hex(b'0'*128) 

1059 raise 

1060 

1061 def connectTree(self, share): 

1062 

1063 # Just in case this came with the full path (maybe an SMB1 client), let's just leave 

1064 # the sharename, we'll take care of the rest 

1065 

1066 #print self._Session['TreeConnectTable'] 

1067 share = share.split('\\')[-1] 

1068 if share in self._Session['TreeConnectTable']: 

1069 # Already connected, no need to reconnect 

1070 treeEntry = self._Session['TreeConnectTable'][share] 

1071 treeEntry['NumberOfUses'] += 1 

1072 self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1 

1073 return treeEntry['TreeConnectId'] 

1074 

1075 #path = share 

1076 try: 

1077 _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0] 

1078 remoteHost = sockaddr[0] 

1079 except: 

1080 remoteHost = self._Connection['ServerIP'] 

1081 path = '\\\\' + remoteHost + '\\' +share 

1082 

1083 treeConnect = SMB2TreeConnect() 

1084 treeConnect['Buffer'] = path.encode('utf-16le') 

1085 treeConnect['PathLength'] = len(path)*2 

1086 

1087 packet = self.SMB_PACKET() 

1088 packet['Command'] = SMB2_TREE_CONNECT 

1089 packet['Data'] = treeConnect 

1090 packetID = self.sendSMB(packet) 

1091 packet = self.recvSMB(packetID) 

1092 if packet.isValidAnswer(STATUS_SUCCESS): 1092 ↛ exitline 1092 didn't return from function 'connectTree', because the condition on line 1092 was never false

1093 treeConnectResponse = SMB2TreeConnect_Response(packet['Data']) 

1094 treeEntry = copy.deepcopy(TREE_CONNECT) 

1095 treeEntry['ShareName'] = share 

1096 treeEntry['TreeConnectId'] = packet['TreeID'] 

1097 treeEntry['Session'] = packet['SessionID'] 

1098 treeEntry['NumberOfUses'] += 1 

1099 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS: 1099 ↛ 1100line 1099 didn't jump to line 1100, because the condition on line 1099 was never true

1100 treeEntry['IsDfsShare'] = True 

1101 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY: 1101 ↛ 1102line 1101 didn't jump to line 1102, because the condition on line 1101 was never true

1102 treeEntry['IsCAShare'] = True 

1103 

1104 if self._Connection['Dialect'] >= SMB2_DIALECT_30: 

1105 if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA): 1105 ↛ 1106line 1105 didn't jump to line 1106, because the condition on line 1105 was never true

1106 treeEntry['EncryptData'] = True 

1107 # ToDo: This and what follows 

1108 # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a 

1109 # decryption key as specified in section 3.1.4.2, by providing the following inputs and store 

1110 # them in Session.EncryptionKey and Session.DecryptionKey: 

1111 if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT: 1111 ↛ 1112line 1111 didn't jump to line 1112, because the condition on line 1111 was never true

1112 treeEntry['IsScaleoutShare'] = True 

1113 

1114 self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry 

1115 self._Session['TreeConnectTable'][share] = treeEntry 

1116 

1117 return packet['TreeID'] 

1118 

1119 def disconnectTree(self, treeId): 

1120 if (treeId in self._Session['TreeConnectTable']) is False: 1120 ↛ 1121line 1120 didn't jump to line 1121, because the condition on line 1120 was never true

1121 raise SessionError(STATUS_INVALID_PARAMETER) 

1122 

1123 if treeId in self._Session['TreeConnectTable']: 1123 ↛ 1131line 1123 didn't jump to line 1131, because the condition on line 1123 was never false

1124 # More than 1 use? descrease it and return, if not, send the packet 

1125 if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1: 

1126 treeEntry = self._Session['TreeConnectTable'][treeId] 

1127 treeEntry['NumberOfUses'] -= 1 

1128 self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1 

1129 return True 

1130 

1131 packet = self.SMB_PACKET() 

1132 packet['Command'] = SMB2_TREE_DISCONNECT 

1133 packet['TreeID'] = treeId 

1134 treeDisconnect = SMB2TreeDisconnect() 

1135 packet['Data'] = treeDisconnect 

1136 packetID = self.sendSMB(packet) 

1137 packet = self.recvSMB(packetID) 

1138 if packet.isValidAnswer(STATUS_SUCCESS): 1138 ↛ exitline 1138 didn't return from function 'disconnectTree', because the condition on line 1138 was never false

1139 shareName = self._Session['TreeConnectTable'][treeId]['ShareName'] 

1140 del(self._Session['TreeConnectTable'][shareName]) 

1141 del(self._Session['TreeConnectTable'][treeId]) 

1142 filesIDToBeRemoved = [] 

1143 for fileID in list(self._Session['OpenTable'].keys()): 

1144 if self._Session['OpenTable'][fileID]['TreeConnect'] == treeId: 

1145 filesIDToBeRemoved.append(fileID) 

1146 for fileIDToBeRemoved in filesIDToBeRemoved: 

1147 del(self._Session['OpenTable'][fileIDToBeRemoved]) 

1148 return True 

1149 

1150 def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None): 

1151 if (treeId in self._Session['TreeConnectTable']) is False: 1151 ↛ 1152line 1151 didn't jump to line 1152, because the condition on line 1151 was never true

1152 raise SessionError(STATUS_INVALID_PARAMETER) 

1153 

1154 fileName = fileName.replace('/', '\\') 

1155 if len(fileName) > 0: 

1156 fileName = ntpath.normpath(fileName) 

1157 if fileName[0] == '\\': 

1158 fileName = fileName[1:] 

1159 

1160 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 1160 ↛ 1161line 1160 didn't jump to line 1161, because the condition on line 1160 was never true

1161 pathName = fileName 

1162 else: 

1163 pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName 

1164 

1165 fileEntry = copy.deepcopy(FILE) 

1166 fileEntry['LeaseKey'] = uuid.generate() 

1167 fileEntry['LeaseState'] = SMB2_LEASE_NONE 

1168 self.GlobalFileTable[pathName] = fileEntry 

1169 

1170 if self._Connection['Dialect'] >= SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True: 1170 ↛ 1172line 1170 didn't jump to line 1172, because the condition on line 1170 was never true

1171 # Is this file NOT on the root directory? 

1172 if len(fileName.split('\\')) > 2: 

1173 parentDir = ntpath.dirname(pathName) 

1174 if parentDir in self.GlobalFileTable: 

1175 raise Exception("Don't know what to do now! :-o") 

1176 else: 

1177 parentEntry = copy.deepcopy(FILE) 

1178 parentEntry['LeaseKey'] = uuid.generate() 

1179 parentEntry['LeaseState'] = SMB2_LEASE_NONE 

1180 self.GlobalFileTable[parentDir] = parentEntry 

1181 

1182 packet = self.SMB_PACKET() 

1183 packet['Command'] = SMB2_CREATE 

1184 packet['TreeID'] = treeId 

1185 if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: 1185 ↛ 1186line 1185 didn't jump to line 1186, because the condition on line 1185 was never true

1186 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS 

1187 

1188 smb2Create = SMB2Create() 

1189 smb2Create['SecurityFlags'] = 0 

1190 smb2Create['RequestedOplockLevel'] = oplockLevel 

1191 smb2Create['ImpersonationLevel'] = impersonationLevel 

1192 smb2Create['DesiredAccess'] = desiredAccess 

1193 smb2Create['FileAttributes'] = fileAttributes 

1194 smb2Create['ShareAccess'] = shareMode 

1195 smb2Create['CreateDisposition'] = creationDisposition 

1196 smb2Create['CreateOptions'] = creationOptions 

1197 

1198 smb2Create['NameLength'] = len(fileName)*2 

1199 if fileName != '': 

1200 smb2Create['Buffer'] = fileName.encode('utf-16le') 

1201 else: 

1202 smb2Create['Buffer'] = b'\x00' 

1203 

1204 if createContexts is not None: 1204 ↛ 1205line 1204 didn't jump to line 1205, because the condition on line 1204 was never true

1205 contextsBuf = b''.join(x.getData() for x in createContexts) 

1206 smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + len(smb2Create['Buffer']) 

1207 

1208 # pad offset to 8-byte align 

1209 if (smb2Create['CreateContextsOffset'] % 8): 

1210 smb2Create['Buffer'] += b'\x00'*(8-(smb2Create['CreateContextsOffset'] % 8)) 

1211 smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + len(smb2Create['Buffer']) 

1212 

1213 smb2Create['CreateContextsLength'] = len(contextsBuf) 

1214 smb2Create['Buffer'] += contextsBuf 

1215 else: 

1216 smb2Create['CreateContextsOffset'] = 0 

1217 smb2Create['CreateContextsLength'] = 0 

1218 

1219 packet['Data'] = smb2Create 

1220 

1221 packetID = self.sendSMB(packet) 

1222 ans = self.recvSMB(packetID) 

1223 if ans.isValidAnswer(STATUS_SUCCESS): 

1224 createResponse = SMB2Create_Response(ans['Data']) 

1225 

1226 openFile = copy.deepcopy(OPEN) 

1227 openFile['FileID'] = createResponse['FileID'] 

1228 openFile['TreeConnect'] = treeId 

1229 openFile['Oplocklevel'] = oplockLevel 

1230 openFile['Durable'] = False 

1231 openFile['ResilientHandle'] = False 

1232 openFile['LastDisconnectTime'] = 0 

1233 openFile['FileName'] = pathName 

1234 

1235 # ToDo: Complete the OperationBuckets 

1236 if self._Connection['Dialect'] >= SMB2_DIALECT_30: 

1237 openFile['DesiredAccess'] = oplockLevel 

1238 openFile['ShareMode'] = oplockLevel 

1239 openFile['CreateOptions'] = oplockLevel 

1240 openFile['FileAttributes'] = oplockLevel 

1241 openFile['CreateDisposition'] = oplockLevel 

1242 

1243 # ToDo: Process the contexts 

1244 self._Session['OpenTable'][createResponse['FileID'].getData()] = openFile 

1245 

1246 # The client MUST generate a handle for the Open, and it MUST 

1247 # return success and the generated handle to the calling application. 

1248 # In our case, str(FileID) 

1249 return createResponse['FileID'].getData() 

1250 

1251 def close(self, treeId, fileId): 

1252 if (treeId in self._Session['TreeConnectTable']) is False: 1252 ↛ 1253line 1252 didn't jump to line 1253, because the condition on line 1252 was never true

1253 raise SessionError(STATUS_INVALID_PARAMETER) 

1254 if (fileId in self._Session['OpenTable']) is False: 1254 ↛ 1255line 1254 didn't jump to line 1255, because the condition on line 1254 was never true

1255 raise SessionError(STATUS_INVALID_PARAMETER) 

1256 

1257 packet = self.SMB_PACKET() 

1258 packet['Command'] = SMB2_CLOSE 

1259 packet['TreeID'] = treeId 

1260 

1261 smbClose = SMB2Close() 

1262 smbClose['Flags'] = 0 

1263 smbClose['FileID'] = fileId 

1264 

1265 packet['Data'] = smbClose 

1266 

1267 packetID = self.sendSMB(packet) 

1268 ans = self.recvSMB(packetID) 

1269 

1270 if ans.isValidAnswer(STATUS_SUCCESS): 1270 ↛ exitline 1270 didn't return from function 'close', because the condition on line 1270 was never false

1271 del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']]) 

1272 del(self._Session['OpenTable'][fileId]) 

1273 

1274 # ToDo Remove stuff from GlobalFileTable 

1275 return True 

1276 

1277 def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True): 

1278 # IMPORTANT NOTE: As you can see, this was coded as a recursive function 

1279 # Hence, you can exhaust the memory pretty easy ( large bytesToRead ) 

1280 # This function should NOT be used for reading files directly, but another higher 

1281 # level function should be used that will break the read into smaller pieces 

1282 

1283 if (treeId in self._Session['TreeConnectTable']) is False: 1283 ↛ 1284line 1283 didn't jump to line 1284, because the condition on line 1283 was never true

1284 raise SessionError(STATUS_INVALID_PARAMETER) 

1285 if (fileId in self._Session['OpenTable']) is False: 1285 ↛ 1286line 1285 didn't jump to line 1286, because the condition on line 1285 was never true

1286 raise SessionError(STATUS_INVALID_PARAMETER) 

1287 

1288 packet = self.SMB_PACKET() 

1289 packet['Command'] = SMB2_READ 

1290 packet['TreeID'] = treeId 

1291 

1292 if self._Connection['MaxReadSize'] < bytesToRead: 1292 ↛ 1293line 1292 didn't jump to line 1293, because the condition on line 1292 was never true

1293 maxBytesToRead = self._Connection['MaxReadSize'] 

1294 else: 

1295 maxBytesToRead = bytesToRead 

1296 

1297 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

1298 packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) // 65536) 

1299 else: 

1300 maxBytesToRead = min(65536,bytesToRead) 

1301 

1302 smbRead = SMB2Read() 

1303 smbRead['Padding'] = 0x50 

1304 smbRead['FileID'] = fileId 

1305 smbRead['Length'] = maxBytesToRead 

1306 smbRead['Offset'] = offset 

1307 packet['Data'] = smbRead 

1308 

1309 packetID = self.sendSMB(packet) 

1310 ans = self.recvSMB(packetID) 

1311 

1312 if ans.isValidAnswer(STATUS_SUCCESS): 1312 ↛ exitline 1312 didn't return from function 'read', because the condition on line 1312 was never false

1313 readResponse = SMB2Read_Response(ans['Data']) 

1314 retData = readResponse['Buffer'] 

1315 if readResponse['DataRemaining'] > 0: 1315 ↛ 1316line 1315 didn't jump to line 1316, because the condition on line 1315 was never true

1316 retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer) 

1317 return retData 

1318 

1319 def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True): 

1320 # IMPORTANT NOTE: As you can see, this was coded as a recursive function 

1321 # Hence, you can exhaust the memory pretty easy ( large bytesToWrite ) 

1322 # This function should NOT be used for writing directly to files, but another higher 

1323 # level function should be used that will break the writes into smaller pieces 

1324 

1325 if (treeId in self._Session['TreeConnectTable']) is False: 1325 ↛ 1326line 1325 didn't jump to line 1326, because the condition on line 1325 was never true

1326 raise SessionError(STATUS_INVALID_PARAMETER) 

1327 if (fileId in self._Session['OpenTable']) is False: 1327 ↛ 1328line 1327 didn't jump to line 1328, because the condition on line 1327 was never true

1328 raise SessionError(STATUS_INVALID_PARAMETER) 

1329 

1330 packet = self.SMB_PACKET() 

1331 packet['Command'] = SMB2_WRITE 

1332 packet['TreeID'] = treeId 

1333 

1334 if self._Connection['MaxWriteSize'] < bytesToWrite: 1334 ↛ 1335line 1334 didn't jump to line 1335, because the condition on line 1334 was never true

1335 maxBytesToWrite = self._Connection['MaxWriteSize'] 

1336 else: 

1337 maxBytesToWrite = bytesToWrite 

1338 

1339 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

1340 packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) // 65536) 

1341 else: 

1342 maxBytesToWrite = min(65536,bytesToWrite) 

1343 

1344 smbWrite = SMB2Write() 

1345 smbWrite['FileID'] = fileId 

1346 smbWrite['Length'] = maxBytesToWrite 

1347 smbWrite['Offset'] = offset 

1348 smbWrite['WriteChannelInfoOffset'] = 0 

1349 smbWrite['Buffer'] = data[:maxBytesToWrite] 

1350 packet['Data'] = smbWrite 

1351 

1352 packetID = self.sendSMB(packet) 

1353 if waitAnswer is True: 1353 ↛ 1356line 1353 didn't jump to line 1356, because the condition on line 1353 was never false

1354 ans = self.recvSMB(packetID) 

1355 else: 

1356 return maxBytesToWrite 

1357 

1358 if ans.isValidAnswer(STATUS_SUCCESS): 

1359 writeResponse = SMB2Write_Response(ans['Data']) 

1360 bytesWritten = writeResponse['Count'] 

1361 if bytesWritten < bytesToWrite: 1361 ↛ 1362line 1361 didn't jump to line 1362, because the condition on line 1361 was never true

1362 bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer) 

1363 return bytesWritten 

1364 

1365 def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False): 

1366 if (treeId in self._Session['TreeConnectTable']) is False: 1366 ↛ 1367line 1366 didn't jump to line 1367, because the condition on line 1366 was never true

1367 raise SessionError(STATUS_INVALID_PARAMETER) 

1368 if (fileId in self._Session['OpenTable']) is False: 1368 ↛ 1369line 1368 didn't jump to line 1369, because the condition on line 1368 was never true

1369 raise SessionError(STATUS_INVALID_PARAMETER) 

1370 

1371 packet = self.SMB_PACKET() 

1372 packet['Command'] = SMB2_QUERY_DIRECTORY 

1373 packet['TreeID'] = treeId 

1374 

1375 queryDirectory = SMB2QueryDirectory() 

1376 queryDirectory['FileInformationClass'] = informationClass 

1377 if resumeIndex != 0 : 1377 ↛ 1378line 1377 didn't jump to line 1378, because the condition on line 1377 was never true

1378 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED 

1379 queryDirectory['FileIndex'] = resumeIndex 

1380 queryDirectory['FileID'] = fileId 

1381 if maxBufferSize is None: 1381 ↛ 1382line 1381 didn't jump to line 1382, because the condition on line 1381 was never true

1382 maxBufferSize = self._Connection['MaxReadSize'] 

1383 queryDirectory['OutputBufferLength'] = maxBufferSize 

1384 queryDirectory['FileNameLength'] = len(searchString)*2 

1385 queryDirectory['Buffer'] = searchString.encode('utf-16le') 

1386 

1387 packet['Data'] = queryDirectory 

1388 

1389 if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True: 

1390 packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) // 65536) 

1391 

1392 packetID = self.sendSMB(packet) 

1393 ans = self.recvSMB(packetID) 

1394 if ans.isValidAnswer(STATUS_SUCCESS): 

1395 queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data']) 

1396 return queryDirectoryResponse['Buffer'] 

1397 

1398 def echo(self): 

1399 packet = self.SMB_PACKET() 

1400 packet['Command'] = SMB2_ECHO 

1401 smbEcho = SMB2Echo() 

1402 packet['Data'] = smbEcho 

1403 packetID = self.sendSMB(packet) 

1404 ans = self.recvSMB(packetID) 

1405 if ans.isValidAnswer(STATUS_SUCCESS): 

1406 return True 

1407 

1408 def cancel(self, packetID): 

1409 packet = self.SMB_PACKET() 

1410 packet['Command'] = SMB2_CANCEL 

1411 packet['MessageID'] = packetID 

1412 

1413 smbCancel = SMB2Cancel() 

1414 

1415 packet['Data'] = smbCancel 

1416 self.sendSMB(packet) 

1417 

1418 def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1): 

1419 if (treeId in self._Session['TreeConnectTable']) is False: 

1420 raise SessionError(STATUS_INVALID_PARAMETER) 

1421 if fileId is None: 

1422 fileId = '\xff'*16 

1423 else: 

1424 if (fileId in self._Session['OpenTable']) is False: 

1425 raise SessionError(STATUS_INVALID_PARAMETER) 

1426 

1427 packet = self.SMB_PACKET() 

1428 packet['Command'] = SMB2_IOCTL 

1429 packet['TreeID'] = treeId 

1430 

1431 smbIoctl = SMB2Ioctl() 

1432 smbIoctl['FileID'] = fileId 

1433 smbIoctl['CtlCode'] = ctlCode 

1434 smbIoctl['MaxInputResponse'] = maxInputResponse 

1435 smbIoctl['MaxOutputResponse'] = maxOutputResponse 

1436 smbIoctl['InputCount'] = len(inputBlob) 

1437 if len(inputBlob) == 0: 

1438 smbIoctl['InputOffset'] = 0 

1439 smbIoctl['Buffer'] = '\x00' 

1440 else: 

1441 smbIoctl['Buffer'] = inputBlob 

1442 smbIoctl['OutputOffset'] = 0 

1443 smbIoctl['MaxOutputResponse'] = maxOutputResponse 

1444 smbIoctl['Flags'] = flags 

1445 

1446 packet['Data'] = smbIoctl 

1447 

1448 packetID = self.sendSMB(packet) 

1449 

1450 if waitAnswer == 0: 

1451 return True 

1452 

1453 ans = self.recvSMB(packetID) 

1454 

1455 if ans.isValidAnswer(STATUS_SUCCESS): 

1456 smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 

1457 return smbIoctlResponse['Buffer'] 

1458 

1459 def flush(self,treeId, fileId): 

1460 if (treeId in self._Session['TreeConnectTable']) is False: 

1461 raise SessionError(STATUS_INVALID_PARAMETER) 

1462 if (fileId in self._Session['OpenTable']) is False: 

1463 raise SessionError(STATUS_INVALID_PARAMETER) 

1464 

1465 packet = self.SMB_PACKET() 

1466 packet['Command'] = SMB2_FLUSH 

1467 packet['TreeID'] = treeId 

1468 

1469 smbFlush = SMB2Flush() 

1470 smbFlush['FileID'] = fileId 

1471 

1472 packet['Data'] = smbFlush 

1473 

1474 packetID = self.sendSMB(packet) 

1475 ans = self.recvSMB(packetID) 

1476 

1477 if ans.isValidAnswer(STATUS_SUCCESS): 

1478 return True 

1479 

1480 def lock(self, treeId, fileId, locks, lockSequence = 0): 

1481 if (treeId in self._Session['TreeConnectTable']) is False: 

1482 raise SessionError(STATUS_INVALID_PARAMETER) 

1483 if (fileId in self._Session['OpenTable']) is False: 

1484 raise SessionError(STATUS_INVALID_PARAMETER) 

1485 

1486 packet = self.SMB_PACKET() 

1487 packet['Command'] = SMB2_LOCK 

1488 packet['TreeID'] = treeId 

1489 

1490 smbLock = SMB2Lock() 

1491 smbLock['FileID'] = fileId 

1492 smbLock['LockCount'] = len(locks) 

1493 smbLock['LockSequence'] = lockSequence 

1494 smbLock['Locks'] = ''.join(str(x) for x in locks) 

1495 

1496 packet['Data'] = smbLock 

1497 

1498 packetID = self.sendSMB(packet) 

1499 ans = self.recvSMB(packetID) 

1500 

1501 if ans.isValidAnswer(STATUS_SUCCESS): 

1502 smbFlushResponse = SMB2Lock_Response(ans['Data']) 

1503 return True 

1504 

1505 # ToDo: 

1506 # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST 

1507 # do the following: 

1508 # The client MUST scan through Open.OperationBuckets and find an element with its Free field 

1509 # set to TRUE. If no such element could be found, an implementation-specific error MUST be 

1510 # returned to the application. 

1511 # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and 

1512 # let BucketNumber = BucketIndex +1. 

1513 # Set Open.OperationBuckets[BucketIndex].Free = FALSE 

1514 # Let the SequenceNumber of the element chosen above be referred to as BucketSequence. 

1515 # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) + 

1516 # BucketSequence. 

1517 # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic. 

1518 

1519 def logoff(self): 

1520 packet = self.SMB_PACKET() 

1521 packet['Command'] = SMB2_LOGOFF 

1522 

1523 smbLogoff = SMB2Logoff() 

1524 

1525 packet['Data'] = smbLogoff 

1526 

1527 packetID = self.sendSMB(packet) 

1528 ans = self.recvSMB(packetID) 

1529 

1530 if ans.isValidAnswer(STATUS_SUCCESS): 1530 ↛ exitline 1530 didn't return from function 'logoff', because the condition on line 1530 was never false

1531 # We clean the stuff we used in case we want to authenticate again 

1532 # within the same connection 

1533 self._Session['UserCredentials'] = '' 

1534 self._Session['Connection'] = 0 

1535 self._Session['SessionID'] = 0 

1536 self._Session['SigningRequired'] = False 

1537 self._Session['SigningKey'] = '' 

1538 self._Session['SessionKey'] = '' 

1539 self._Session['SigningActivated'] = False 

1540 return True 

1541 

1542 def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ): 

1543 if (treeId in self._Session['TreeConnectTable']) is False: 1543 ↛ 1544line 1543 didn't jump to line 1544, because the condition on line 1543 was never true

1544 raise SessionError(STATUS_INVALID_PARAMETER) 

1545 if (fileId in self._Session['OpenTable']) is False: 1545 ↛ 1546line 1545 didn't jump to line 1546, because the condition on line 1545 was never true

1546 raise SessionError(STATUS_INVALID_PARAMETER) 

1547 

1548 packet = self.SMB_PACKET() 

1549 packet['Command'] = SMB2_QUERY_INFO 

1550 packet['TreeID'] = treeId 

1551 

1552 queryInfo = SMB2QueryInfo() 

1553 queryInfo['FileID'] = fileId 

1554 queryInfo['InfoType'] = infoType 

1555 queryInfo['FileInfoClass'] = fileInfoClass 

1556 queryInfo['OutputBufferLength'] = 65535 

1557 queryInfo['AdditionalInformation'] = additionalInformation 

1558 if len(inputBlob) == 0: 1558 ↛ 1562line 1558 didn't jump to line 1562, because the condition on line 1558 was never false

1559 queryInfo['InputBufferOffset'] = 0 

1560 queryInfo['Buffer'] = '\x00' 

1561 else: 

1562 queryInfo['InputBufferLength'] = len(inputBlob) 

1563 queryInfo['Buffer'] = inputBlob 

1564 queryInfo['Flags'] = flags 

1565 

1566 packet['Data'] = queryInfo 

1567 packetID = self.sendSMB(packet) 

1568 ans = self.recvSMB(packetID) 

1569 

1570 if ans.isValidAnswer(STATUS_SUCCESS): 1570 ↛ exitline 1570 didn't return from function 'queryInfo', because the condition on line 1570 was never false

1571 queryResponse = SMB2QueryInfo_Response(ans['Data']) 

1572 return queryResponse['Buffer'] 

1573 

1574 def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ): 

1575 if (treeId in self._Session['TreeConnectTable']) is False: 1575 ↛ 1576line 1575 didn't jump to line 1576, because the condition on line 1575 was never true

1576 raise SessionError(STATUS_INVALID_PARAMETER) 

1577 if (fileId in self._Session['OpenTable']) is False: 1577 ↛ 1578line 1577 didn't jump to line 1578, because the condition on line 1577 was never true

1578 raise SessionError(STATUS_INVALID_PARAMETER) 

1579 

1580 packet = self.SMB_PACKET() 

1581 packet['Command'] = SMB2_SET_INFO 

1582 packet['TreeID'] = treeId 

1583 

1584 setInfo = SMB2SetInfo() 

1585 setInfo['InfoType'] = infoType 

1586 setInfo['FileInfoClass'] = fileInfoClass 

1587 setInfo['BufferLength'] = len(inputBlob) 

1588 setInfo['AdditionalInformation'] = additionalInformation 

1589 setInfo['FileID'] = fileId 

1590 setInfo['Buffer'] = inputBlob 

1591 

1592 packet['Data'] = setInfo 

1593 packetID = self.sendSMB(packet) 

1594 ans = self.recvSMB(packetID) 

1595 

1596 if ans.isValidAnswer(STATUS_SUCCESS): 

1597 return True 

1598 

1599 def getSessionKey(self): 

1600 if self.getDialect() >= SMB2_DIALECT_30: 

1601 return self._Session['ApplicationKey'] 

1602 else: 

1603 return self._Session['SessionKey'] 

1604 

1605 def setSessionKey(self, key): 

1606 if self.getDialect() >= SMB2_DIALECT_30: 

1607 self._Session['ApplicationKey'] = key 

1608 else: 

1609 self._Session['SessionKey'] = key 

1610 

1611 ###################################################################### 

1612 # Higher level functions 

1613 

1614 def rename(self, shareName, oldPath, newPath): 

1615 oldPath = oldPath.replace('/', '\\') 

1616 oldPath = ntpath.normpath(oldPath) 

1617 if len(oldPath) > 0 and oldPath[0] == '\\': 1617 ↛ 1620line 1617 didn't jump to line 1620, because the condition on line 1617 was never false

1618 oldPath = oldPath[1:] 

1619 

1620 newPath = newPath.replace('/', '\\') 

1621 newPath = ntpath.normpath(newPath) 

1622 if len(newPath) > 0 and newPath[0] == '\\': 1622 ↛ 1625line 1622 didn't jump to line 1625, because the condition on line 1622 was never false

1623 newPath = newPath[1:] 

1624 

1625 treeId = self.connectTree(shareName) 

1626 fileId = None 

1627 try: 

1628 fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0) 

1629 renameReq = FILE_RENAME_INFORMATION_TYPE_2() 

1630 renameReq['ReplaceIfExists'] = 1 

1631 renameReq['RootDirectory'] = '\x00'*8 

1632 renameReq['FileNameLength'] = len(newPath)*2 

1633 renameReq['FileName'] = newPath.encode('utf-16le') 

1634 self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO) 

1635 finally: 

1636 if fileId is not None: 1636 ↛ 1638line 1636 didn't jump to line 1638, because the condition on line 1636 was never false

1637 self.close(treeId, fileId) 

1638 self.disconnectTree(treeId) 

1639 

1640 return True 

1641 

1642 def writeFile(self, treeId, fileId, data, offset = 0): 

1643 finished = False 

1644 writeOffset = offset 

1645 while not finished: 1645 ↛ 1652line 1645 didn't jump to line 1652, because the condition on line 1645 was never false

1646 if len(data) == 0: 

1647 break 

1648 writeData = data[:self._Connection['MaxWriteSize']] 

1649 data = data[self._Connection['MaxWriteSize']:] 

1650 written = self.write(treeId, fileId, writeData, writeOffset, len(writeData)) 

1651 writeOffset += written 

1652 return writeOffset - offset 

1653 

1654 def isSnapshotRequest(self, path): 

1655 #TODO: use a regex here? 

1656 return '@GMT-' in path 

1657 

1658 def timestampForSnapshot(self, path): 

1659 timestamp = path[path.index("@GMT-"):path.index("@GMT-")+24] 

1660 path = path.replace(timestamp, '') 

1661 from datetime import datetime 

1662 fTime = int((datetime.strptime(timestamp, '@GMT-%Y.%m.%d-%H.%M.%S') - datetime(1970,1,1)).total_seconds()) 

1663 fTime *= 10000000 

1664 fTime += 116444736000000000 

1665 

1666 token = SMB2_CREATE_TIMEWARP_TOKEN() 

1667 token['Timestamp'] = fTime 

1668 

1669 ctx = SMB2CreateContext() 

1670 ctx['Next'] = 0 

1671 ctx['NameOffset'] = 16 

1672 ctx['NameLength'] = len('TWrp') 

1673 ctx['DataOffset'] = 24 

1674 ctx['DataLength'] = 8 

1675 ctx['Buffer'] = b'TWrp' 

1676 ctx['Buffer'] += b'\x00'*4 # 4 bytes to 8-byte align 

1677 ctx['Buffer'] += token.getData() 

1678 

1679 # fix-up the path 

1680 path = path.replace(timestamp, '').replace('\\\\', '\\') 

1681 if path == '\\': 

1682 path += '*' 

1683 return path, ctx 

1684 

1685 def listPath(self, shareName, path, password = None): 

1686 createContexts = None 

1687 

1688 if self.isSnapshotRequest(path): 1688 ↛ 1689line 1688 didn't jump to line 1689, because the condition on line 1688 was never true

1689 createContexts = [] 

1690 path, ctx = self.timestampForSnapshot(path) 

1691 createContexts.append(ctx) 

1692 

1693 # ToDo: Handle situations where share is password protected 

1694 path = path.replace('/', '\\') 

1695 path = ntpath.normpath(path) 

1696 if len(path) > 0 and path[0] == '\\': 1696 ↛ 1697line 1696 didn't jump to line 1697, because the condition on line 1696 was never true

1697 path = path[1:] 

1698 

1699 treeId = self.connectTree(shareName) 

1700 

1701 fileId = None 

1702 try: 

1703 # ToDo, we're assuming it's a directory, we should check what the file type is 

1704 fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_SHARE_READ | 

1705 FILE_SHARE_WRITE | FILE_SHARE_DELETE, 

1706 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0, 

1707 createContexts=createContexts) 

1708 res = '' 

1709 files = [] 

1710 from impacket import smb 

1711 while True: 

1712 try: 

1713 res = self.queryDirectory(treeId, fileId, ntpath.basename(path), maxBufferSize=65535, 

1714 informationClass=FILE_FULL_DIRECTORY_INFORMATION) 

1715 nextOffset = 1 

1716 while nextOffset != 0: 

1717 fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE) 

1718 fileInfo.fromString(res) 

1719 files.append(smb.SharedFile(fileInfo['CreationTime'], fileInfo['LastAccessTime'], 

1720 fileInfo['LastChangeTime'], fileInfo['EndOfFile'], 

1721 fileInfo['AllocationSize'], fileInfo['ExtFileAttributes'], 

1722 fileInfo['FileName'].decode('utf-16le'), 

1723 fileInfo['FileName'].decode('utf-16le'))) 

1724 nextOffset = fileInfo['NextEntryOffset'] 

1725 res = res[nextOffset:] 

1726 except SessionError as e: 1726 ↛ 1730line 1726 didn't jump to line 1730

1727 if (e.get_error_code()) != STATUS_NO_MORE_FILES: 1727 ↛ 1728line 1727 didn't jump to line 1728, because the condition on line 1727 was never true

1728 raise 

1729 break 

1730 except Exception as e: 

1731 print(str(e)) 

1732 raise 

1733 finally: 

1734 if fileId is not None: 1734 ↛ 1736line 1734 didn't jump to line 1736, because the condition on line 1734 was never false

1735 self.close(treeId, fileId) 

1736 self.disconnectTree(treeId) 1736 ↛ exitline 1736 didn't except from function 'listPath', because the raise on line 1728 wasn't executed or the raise on line 1732 wasn't executed

1737 

1738 return files 

1739 

1740 def mkdir(self, shareName, pathName, password = None): 

1741 # ToDo: Handle situations where share is password protected 

1742 pathName = pathName.replace('/', '\\') 

1743 pathName = ntpath.normpath(pathName) 

1744 if len(pathName) > 0 and pathName[0] == '\\': 1744 ↛ 1747line 1744 didn't jump to line 1747, because the condition on line 1744 was never false

1745 pathName = pathName[1:] 

1746 

1747 treeId = self.connectTree(shareName) 

1748 

1749 fileId = None 

1750 try: 

1751 fileId = self.create(treeId, pathName, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 

1752 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0) 

1753 finally: 

1754 if fileId is not None: 1754 ↛ 1756line 1754 didn't jump to line 1756, because the condition on line 1754 was never false

1755 self.close(treeId, fileId) 

1756 self.disconnectTree(treeId) 

1757 

1758 return True 

1759 

1760 def rmdir(self, shareName, pathName, password = None): 

1761 # ToDo: Handle situations where share is password protected 

1762 pathName = pathName.replace('/', '\\') 

1763 pathName = ntpath.normpath(pathName) 

1764 if len(pathName) > 0 and pathName[0] == '\\': 1764 ↛ 1767line 1764 didn't jump to line 1767, because the condition on line 1764 was never false

1765 pathName = pathName[1:] 

1766 

1767 treeId = self.connectTree(shareName) 

1768 

1769 fileId = None 

1770 try: 

1771 fileId = self.create(treeId, pathName, desiredAccess=DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 

1772 shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 

1773 creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, 

1774 creationDisposition=FILE_OPEN, fileAttributes=0) 

1775 from impacket import smb 

1776 delete_req = smb.SMBSetFileDispositionInfo() 

1777 delete_req['DeletePending'] = True 

1778 self.setInfo(treeId, fileId, inputBlob=delete_req, fileInfoClass=SMB2_FILE_DISPOSITION_INFO) 

1779 finally: 

1780 if fileId is not None: 1780 ↛ 1782line 1780 didn't jump to line 1782, because the condition on line 1780 was never false

1781 self.close(treeId, fileId) 

1782 self.disconnectTree(treeId) 

1783 

1784 return True 

1785 

1786 def remove(self, shareName, pathName, password = None): 

1787 # ToDo: Handle situations where share is password protected 

1788 pathName = pathName.replace('/', '\\') 

1789 pathName = ntpath.normpath(pathName) 

1790 if len(pathName) > 0 and pathName[0] == '\\': 

1791 pathName = pathName[1:] 

1792 

1793 treeId = self.connectTree(shareName) 

1794 

1795 fileId = None 

1796 try: 

1797 fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0) 

1798 finally: 

1799 if fileId is not None: 1799 ↛ 1801line 1799 didn't jump to line 1801, because the condition on line 1799 was never false

1800 self.close(treeId, fileId) 

1801 self.disconnectTree(treeId) 

1802 

1803 return True 

1804 

1805 def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ): 

1806 createContexts = None 

1807 

1808 if self.isSnapshotRequest(path): 1808 ↛ 1809line 1808 didn't jump to line 1809, because the condition on line 1808 was never true

1809 createContexts = [] 

1810 path, ctx = self.timestampForSnapshot(path) 

1811 createContexts.append(ctx) 

1812 

1813 # ToDo: Handle situations where share is password protected 

1814 path = path.replace('/', '\\') 

1815 path = ntpath.normpath(path) 

1816 if len(path) > 0 and path[0] == '\\': 

1817 path = path[1:] 

1818 

1819 treeId = self.connectTree(shareName) 

1820 fileId = None 

1821 from impacket import smb 

1822 try: 

1823 fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0, createContexts=createContexts) 

1824 res = self.queryInfo(treeId, fileId) 

1825 fileInfo = smb.SMBQueryFileStandardInfo(res) 

1826 fileSize = fileInfo['EndOfFile'] 

1827 if (fileSize-offset) < self._Connection['MaxReadSize']: 

1828 # Skip reading 0 bytes files. 

1829 if (fileSize-offset) > 0: 1829 ↛ 1841line 1829 didn't jump to line 1841, because the condition on line 1829 was never false

1830 data = self.read(treeId, fileId, offset, fileSize-offset) 

1831 callback(data) 

1832 else: 

1833 written = 0 

1834 toBeRead = fileSize-offset 

1835 while written < toBeRead: 

1836 data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize']) 

1837 written += len(data) 

1838 offset += len(data) 

1839 callback(data) 

1840 finally: 

1841 if fileId is not None: 1841 ↛ 1843line 1841 didn't jump to line 1843, because the condition on line 1841 was never false

1842 self.close(treeId, fileId) 

1843 self.disconnectTree(treeId) 

1844 

1845 def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE): 

1846 # ToDo: Handle situations where share is password protected 

1847 path = path.replace('/', '\\') 

1848 path = ntpath.normpath(path) 

1849 if len(path) > 0 and path[0] == '\\': 1849 ↛ 1852line 1849 didn't jump to line 1852, because the condition on line 1849 was never false

1850 path = path[1:] 

1851 

1852 treeId = self.connectTree(shareName) 

1853 fileId = None 

1854 try: 

1855 fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0) 

1856 finished = False 

1857 writeOffset = offset 

1858 while not finished: 1858 ↛ 1865line 1858 didn't jump to line 1865, because the condition on line 1858 was never false

1859 data = callback(self._Connection['MaxWriteSize']) 

1860 if len(data) == 0: 

1861 break 

1862 written = self.write(treeId, fileId, data, writeOffset, len(data)) 

1863 writeOffset += written 

1864 finally: 

1865 if fileId is not None: 1865 ↛ 1867line 1865 didn't jump to line 1867, because the condition on line 1865 was never false

1866 self.close(treeId, fileId) 

1867 self.disconnectTree(treeId) 

1868 

1869 def waitNamedPipe(self, treeId, pipename, timeout = 5): 

1870 pipename = ntpath.basename(pipename) 

1871 if (treeId in self._Session['TreeConnectTable']) is False: 

1872 raise SessionError(STATUS_INVALID_PARAMETER) 

1873 if len(pipename) > 0xffff: 

1874 raise SessionError(STATUS_INVALID_PARAMETER) 

1875 

1876 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE() 

1877 pipeWait['Timeout'] = timeout*100000 

1878 pipeWait['NameLength'] = len(pipename)*2 

1879 pipeWait['TimeoutSpecified'] = 1 

1880 pipeWait['Name'] = pipename.encode('utf-16le') 

1881 

1882 return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0) 

1883 

1884 def getIOCapabilities(self): 

1885 res = dict() 

1886 

1887 res['MaxReadSize'] = self._Connection['MaxReadSize'] 

1888 res['MaxWriteSize'] = self._Connection['MaxWriteSize'] 

1889 return res 

1890 

1891 

1892 ###################################################################### 

1893 # Backward compatibility functions and alias for SMB1 and DCE Transports 

1894 # NOTE: It is strongly recommended not to use these commands 

1895 # when implementing new client calls. 

1896 get_server_name = getServerName 

1897 get_client_name = getClientName 

1898 get_server_domain = getServerDomain 

1899 get_server_dns_domain_name = getServerDNSDomainName 

1900 get_server_dns_host_name = getServerDNSHostName 

1901 get_remote_name = getRemoteName 

1902 set_remote_name = setRemoteName 

1903 get_remote_host = getServerIP 

1904 get_server_os = getServerOS 

1905 get_server_os_major = getServerOSMajor 

1906 get_server_os_minor = getServerOSMinor 

1907 get_server_os_build = getServerOSBuild 

1908 tree_connect_andx = connectTree 

1909 tree_connect = connectTree 

1910 connect_tree = connectTree 

1911 disconnect_tree = disconnectTree 

1912 set_timeout = setTimeout 

1913 use_timeout = useTimeout 

1914 stor_file = storeFile 

1915 retr_file = retrieveFile 

1916 list_path = listPath 

1917 

1918 def close_session(self): 

1919 if self._NetBIOSSession: 1919 ↛ exitline 1919 didn't return from function 'close_session', because the condition on line 1919 was never false

1920 self._NetBIOSSession.close() 

1921 self._NetBIOSSession = None 

1922 

1923 def doesSupportNTLMv2(self): 

1924 # Always true :P 

1925 return True 

1926 

1927 def is_login_required(self): 

1928 # Always true :P 

1929 return True 

1930 

1931 def is_signing_required(self): 

1932 return self._Connection['RequireSigning'] 

1933 

1934 def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None): 

1935 if len(fileName) > 0 and fileName[0] == '\\': 

1936 fileName = fileName[1:] 

1937 

1938 if cmd is not None: 

1939 from impacket import smb 

1940 ntCreate = smb.SMBCommand(data = cmd.getData()) 

1941 params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters']) 

1942 return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'], 

1943 params['CreateOptions'], params['Disposition'], params['FileAttributes'], 

1944 params['Impersonation'], params['SecurityFlags']) 

1945 

1946 else: 

1947 return self.create(treeId, fileName, 

1948 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | 

1949 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL, 

1950 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 ) 

1951 

1952 def get_socket(self): 

1953 return self._NetBIOSSession.get_socket() 

1954 

1955 

1956 def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None): 

1957 # ToDo: Handle the custom smb_packet situation 

1958 return self.write(tid, fid, data, offset, len(data)) 

1959 

1960 def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0): 

1961 return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer) 

1962 

1963 def TransactNamedPipeRecv(self): 

1964 ans = self.recvSMB() 

1965 

1966 if ans.isValidAnswer(STATUS_SUCCESS): 

1967 smbIoctlResponse = SMB2Ioctl_Response(ans['Data']) 

1968 return smbIoctlResponse['Buffer'] 

1969 

1970 

1971 def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None): 

1972 # ToDo: Handle the custom smb_packet situation 

1973 if max_size is None: 1973 ↛ 1974line 1973 didn't jump to line 1974, because the condition on line 1973 was never true

1974 max_size = self._Connection['MaxReadSize'] 

1975 return self.read(tid, fid, offset, max_size, wait_answer) 

1976 

1977 def list_shared(self): 

1978 # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED 

1979 raise SessionError(STATUS_NOT_IMPLEMENTED) 

1980 

1981 def open_andx(self, tid, fileName, open_mode, desired_access): 

1982 # ToDo Return all the attributes of the file 

1983 if len(fileName) > 0 and fileName[0] == '\\': 

1984 fileName = fileName[1:] 

1985 

1986 fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0) 

1987 return fileId, 0, 0, 0, 0, 0, 0, 0, 0 

1988 

1989 def set_session_key(self, signingKey): 

1990 self._Session['SessionKey'] = signingKey 

1991 self._Session['SigningActivated'] = True 

1992 self._Session['SigningRequired'] = True 

1993 

1994 def set_hostname_validation(self, validate, accept_empty, hostname): 

1995 self._strict_hostname_validation = validate 

1996 self._validation_allow_absent = accept_empty 

1997 self._accepted_hostname = hostname 

1998 

1999 def perform_hostname_validation(self): 

2000 if self._Session['ServerName'] == '': 

2001 if not self._validation_allow_absent: 

2002 raise self.HostnameValidationException('Hostname was not supplied by target host and absent validation is disallowed') 

2003 return 

2004 if self._Session['ServerName'].lower() != self._accepted_hostname.lower() and self._Session['ServerDNSHostName'].lower() != self._accepted_hostname.lower(): 

2005 raise self.HostnameValidationException('Supplied hostname %s does not match reported hostnames %s or %s' % 

2006 (self._accepted_hostname.lower(), self._Session['ServerName'].lower(), self._Session['ServerDNSHostName'].lower()))