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# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

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

7# for more information. 

8# 

9# Description: 

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

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

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

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

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

15# it certainly helps following the document way easier. 

16# 

17# Author: 

18# Alberto Solino (@agsolino) 

19# 

20# ToDo: 

21# [X] Implement SMB2_CHANGE_NOTIFY 

22# [X] Implement SMB2_QUERY_INFO 

23# [X] Implement SMB2_SET_INFO 

24# [ ] Implement SMB2_OPLOCK_BREAK 

25# [X] Implement SMB3 signing 

26# [X] Implement SMB3 encryption 

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

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

29# 

30 

31from __future__ import division 

32from __future__ import print_function 

33 

34import socket 

35import ntpath 

36import random 

37import string 

38import struct 

39from six import indexbytes, b 

40from binascii import a2b_hex 

41from contextlib import contextmanager 

42from pyasn1.type.univ import noValue 

43from Cryptodome.Cipher import AES 

44 

45from impacket import nmb, ntlm, uuid, crypto 

46from impacket.smb3structs import * 

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

48 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES 

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

50from impacket.krb5.gssapi import KRB5_AP_REQ 

51 

52 

53# For signing 

54import hashlib, hmac, copy 

55 

56# Our random number generator 

57try: 

58 rand = random.SystemRandom() 

59except NotImplementedError: 

60 rand = random 

61 pass 

62 

63# Structs to be used 

64TREE_CONNECT = { 

65 'ShareName' : '', 

66 'TreeConnectId' : 0, 

67 'Session' : 0, 

68 'IsDfsShare' : False, 

69 # If the client implements the SMB 3.0 dialect, 

70 # the client MUST also implement the following 

71 'IsCAShare' : False, 

72 'EncryptData' : False, 

73 'IsScaleoutShare' : False, 

74 # Outside the protocol 

75 'NumberOfUses' : 0, 

76} 

77 

78FILE = { 

79 'OpenTable' : [], 

80 'LeaseKey' : '', 

81 'LeaseState' : 0, 

82 'LeaseEpoch' : 0, 

83} 

84 

85OPEN = { 

86 'FileID' : '', 

87 'TreeConnect' : 0, 

88 'Connection' : 0, # Not Used 

89 'Oplocklevel' : 0, 

90 'Durable' : False, 

91 'FileName' : '', 

92 'ResilientHandle' : False, 

93 'LastDisconnectTime' : 0, 

94 'ResilientTimeout' : 0, 

95 'OperationBuckets' : [], 

96 # If the client implements the SMB 3.0 dialect, 

97 # the client MUST implement the following 

98 'CreateGuid' : '', 

99 'IsPersistent' : False, 

100 'DesiredAccess' : '', 

101 'ShareMode' : 0, 

102 'CreateOption' : '', 

103 'FileAttributes' : '', 

104 'CreateDisposition' : '', 

105} 

106 

107REQUEST = { 

108 'CancelID' : '', 

109 'Message' : '', 

110 'Timestamp' : 0, 

111} 

112 

113CHANNEL = { 

114 'SigningKey' : '', 

115 'Connection' : 0, 

116} 

117 

118 

119class SessionError(Exception): 

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

121 Exception.__init__(self) 

122 self.error = error 

123 self.packet = packet 

124 

125 def get_error_code( self ): 

126 return self.error 

127 

128 def get_error_packet( self ): 

129 return self.packet 

130 

131 def __str__( self ): 

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

133 

134 

135class SMB3: 

136 class HostnameValidationException(Exception): 

137 pass 

138 

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

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

141 

142 # [MS-SMB2] Section 3 

143 self.RequireMessageSigning = False # 

144 self.ConnectionTable = {} 

145 self.GlobalFileTable = {} 

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

147 # Only for SMB 3.0 

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

149 self.MaxDialect = [] 

150 self.RequireSecureNegotiate = False 

151 

152 # Per Transport Connection Data 

153 self._Connection = { 

154 # Indexed by SessionID 

155 #'SessionTable' : {}, 

156 # Indexed by MessageID 

157 'OutstandingRequests' : {}, 

158 'OutstandingResponses' : {}, # 

159 'SequenceWindow' : 0, # 

160 'GSSNegotiateToken' : '', # 

161 'MaxTransactSize' : 0, # 

162 'MaxReadSize' : 0, # 

163 'MaxWriteSize' : 0, # 

164 'ServerGuid' : '', # 

165 'RequireSigning' : False, # 

166 'ServerName' : '', # 

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

168 # also implement the following 

169 'Dialect' : 0, # 

170 'SupportsFileLeasing' : False, # 

171 'SupportsMultiCredit' : False, # 

172 # If the client implements the SMB 3.0 dialect, 

173 # it MUST also implement the following 

174 'SupportsDirectoryLeasing' : False, # 

175 'SupportsMultiChannel' : False, # 

176 'SupportsPersistentHandles': False, # 

177 'SupportsEncryption' : False, # 

178 'ClientCapabilities' : 0, 

179 'ServerCapabilities' : 0, # 

180 'ClientSecurityMode' : 0, # 

181 'ServerSecurityMode' : 0, # 

182 # Outside the protocol 

183 'ServerIP' : '', # 

184 'ClientName' : '', # 

185 #GSSoptions (MutualAuth and Delegate) 

186 'GSSoptions' : {}, 

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

188 } 

189 

190 self._Session = { 

191 'SessionID' : 0, # 

192 'TreeConnectTable' : {}, # 

193 'SessionKey' : b'', # 

194 'SigningRequired' : False, # 

195 'Connection' : 0, # 

196 'UserCredentials' : '', # 

197 'OpenTable' : {}, # 

198 # If the client implements the SMB 3.0 dialect, 

199 # it MUST also implement the following 

200 'ChannelList' : [], 

201 'ChannelSequence' : 0, 

202 #'EncryptData' : False, 

203 'EncryptData' : True, 

204 'EncryptionKey' : '', 

205 'DecryptionKey' : '', 

206 'SigningKey' : '', 

207 'ApplicationKey' : b'', 

208 # Outside the protocol 

209 'SessionFlags' : 0, # 

210 'ServerName' : '', # 

211 'ServerDomain' : '', # 

212 'ServerDNSDomainName' : '', # 

213 'ServerDNSHostName' : '', # 

214 'ServerOS' : '', # 

215 'SigningActivated' : False, # 

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

217 'CalculatePreAuthHash' : True, 

218 } 

219 

220 self.SMB_PACKET = SMB2Packet 

221 

222 self._timeout = timeout 

223 self._Connection['ServerIP'] = remote_host 

224 self._NetBIOSSession = None 

225 self._preferredDialect = preferredDialect 

226 self._doKerberos = False 

227 

228 # Strict host validation - off by default 

229 self._strict_hostname_validation = False 

230 self._validation_allow_absent = True 

231 self._accepted_hostname = '' 

232 

233 self.__userName = '' 

234 self.__password = '' 

235 self.__domain = '' 

236 self.__lmhash = '' 

237 self.__nthash = '' 

238 self.__kdc = '' 

239 self.__aesKey = '' 

240 self.__TGT = None 

241 self.__TGS = None 

242 

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

244 self._Connection['ServerName'] = remote_host 

245 else: 

246 self._Connection['ServerName'] = remote_name 

247 

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

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

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

251 else: 

252 self._Connection['ClientName'] = my_name 

253 

254 if session is None: 

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

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

257 my_name = socket.gethostname() 

258 i = my_name.find('.') 

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

260 my_name = my_name[:i] 

261 

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

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

264 else: 

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

266 

267 self.negotiateSession(preferredDialect) 

268 else: 

269 self._NetBIOSSession = session 

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

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

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

273 self.negotiateSession(preferredDialect, negSessionResponse) 

274 

275 def printStatus(self): 

276 print("CONNECTION") 

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

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

279 print() 

280 print("SESSION") 

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

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

283 

284 def __UpdateConnectionPreAuthHash(self, data): 

285 from Cryptodome.Hash import SHA512 

286 calculatedHash = SHA512.new() 

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

288 calculatedHash.update(data) 

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

290 

291 def __UpdatePreAuthHash(self, data): 

292 from Cryptodome.Hash import SHA512 

293 calculatedHash = SHA512.new() 

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

295 calculatedHash.update(data) 

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

297 

298 def getKerberos(self): 

299 return self._doKerberos 

300 

301 def getServerName(self): 

302 return self._Session['ServerName'] 

303 

304 def getClientName(self): 

305 return self._Session['ClientName'] 

306 

307 def getRemoteName(self): 

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

309 return self._Connection['ServerName'] 

310 return self._Session['ServerName'] 

311 

312 def setRemoteName(self, name): 

313 self._Session['ServerName'] = name 

314 return True 

315 

316 def getServerIP(self): 

317 return self._Connection['ServerIP'] 

318 

319 def getServerDomain(self): 

320 return self._Session['ServerDomain'] 

321 

322 def getServerDNSDomainName(self): 

323 return self._Session['ServerDNSDomainName'] 

324 

325 def getServerDNSHostName(self): 

326 return self._Session['ServerDNSHostName'] 

327 

328 def getServerOS(self): 

329 return self._Session['ServerOS'] 

330 

331 def getServerOSMajor(self): 

332 return self._Session['ServerOSMajor'] 

333 

334 def getServerOSMinor(self): 

335 return self._Session['ServerOSMinor'] 

336 

337 def getServerOSBuild(self): 

338 return self._Session['ServerOSBuild'] 

339 

340 def isGuestSession(self): 

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

342 

343 def setTimeout(self, timeout): 

344 self._timeout = timeout 

345 

346 @contextmanager 

347 def useTimeout(self, timeout): 

348 prev_timeout = self.getTimeout(timeout) 

349 try: 

350 yield 

351 finally: 

352 self.setTimeout(prev_timeout) 

353 

354 def getDialect(self): 

355 return self._Connection['Dialect'] 

356 

357 def signSMB(self, packet): 

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

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

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

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

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

363 else: 

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

365 p = packet.getData() 

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

367 packet['Signature'] = signature 

368 

369 def sendSMB(self, packet): 

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

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

372 

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

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

375 # SMB2 header to Session.ChannelSequence 

376 

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

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

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

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

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

382 

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

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

385 packet['CreditCharge'] = 1 

386 

387 # Standard credit request after negotiating protocol 

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

389 packet['CreditRequestResponse'] = 127 

390 

391 messageId = packet['MessageID'] 

392 

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

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

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

396 packet['Flags'] = SMB2_FLAGS_SIGNED 

397 self.signSMB(packet) 

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

399 packet['Flags'] = SMB2_FLAGS_SIGNED 

400 self.signSMB(packet) 

401 

402 if packet['Command'] is SMB2_NEGOTIATE: 

403 data = packet.getData() 

404 self.__UpdateConnectionPreAuthHash(data) 

405 self._Session['CalculatePreAuthHash'] = False 

406 

407 if packet['Command'] is SMB2_SESSION_SETUP: 

408 self._Session['CalculatePreAuthHash'] = True 

409 

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

411 plainText = packet.getData() 

412 transformHeader = SMB2_TRANSFORM_HEADER() 

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

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

415 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM 

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

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

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

419 cipherText = cipher.encrypt(plainText) 

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

421 packet = transformHeader.getData() + cipherText 

422 

423 self._NetBIOSSession.send_packet(packet) 

424 else: 

425 data = packet.getData() 

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

427 self.__UpdatePreAuthHash(data) 

428 

429 self._NetBIOSSession.send_packet(data) 

430 

431 return messageId 

432 

433 def recvSMB(self, packetID = None): 

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

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

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

437 

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

439 

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

441 # Packet is encrypted 

442 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

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

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

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

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

447 packet = SMB2Packet(plainText) 

448 else: 

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

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

451 # see [MS-ERREF] section 2.3. 

452 packet = SMB2Packet(data.get_trailer()) 

453 

454 # Loop while we receive pending requests 

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

456 status = STATUS_PENDING 

457 while status == STATUS_PENDING: 

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

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

460 packet = SMB2Packet(data.get_trailer()) 

461 else: 

462 # Packet is encrypted 

463 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer()) 

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

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

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

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

468 packet = SMB2Packet(plainText) 

469 status = packet['Status'] 

470 

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

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

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

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

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

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

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

478 return packet 

479 else: 

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

481 return self.recvSMB(packetID) 

482 

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

484 # Let's store some data for later use 

485 self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

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

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

488 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION 

489 currentDialect = SMB2_DIALECT_WILDCARD 

490 

491 # Do we have a negSessionPacket already? 

492 if negSessionResponse is not None: 

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

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

495 currentDialect = negResp['DialectRevision'] 

496 

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

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

499 

500 packet = self.SMB_PACKET() 

501 packet['Command'] = SMB2_NEGOTIATE 

502 negSession = SMB2Negotiate() 

503 

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

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

506 negSession['ClientGuid'] = self.ClientGuid 

507 if preferredDialect is not None: 

508 negSession['Dialects'] = [preferredDialect] 

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

510 # Build the Contexts 

511 contextData = SMB311ContextData() 

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

513 contextData['NegotiateContextCount'] = 0 

514 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_PREAUTH_INTEGRITY_CAPABILITIES 

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

516 negotiateContext = SMB2NegotiateContext() 

517 negotiateContext['ContextType'] = SMB2_PREAUTH_INTEGRITY_CAPABILITIES 

518 

519 preAuthIntegrityCapabilities = SMB2PreAuthIntegrityCapabilities() 

520 preAuthIntegrityCapabilities['HashAlgorithmCount'] = 1 

521 preAuthIntegrityCapabilities['SaltLength'] = 32 

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

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

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

525 

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

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

528 contextData['NegotiateContextCount'] += 1 

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

530 

531 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_ENCRYPTION_CAPABILITIES 

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

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

534 

535 negotiateContext2 = SMB2NegotiateContext () 

536 negotiateContext2['ContextType'] = SMB2_ENCRYPTION_CAPABILITIES 

537 

538 encryptionCapabilities = SMB2EncryptionCapabilities() 

539 encryptionCapabilities['CipherCount'] = 1 

540 encryptionCapabilities['Ciphers'] = 1 

541 

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

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

544 contextData['NegotiateContextCount'] += 1 

545 

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

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

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

549 # previous negotiate context. 

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

551 

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

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

554 

555 else: 

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

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

558 packet['Data'] = negSession 

559 

560 packetID = self.sendSMB(packet) 

561 ans = self.recvSMB(packetID) 

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

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

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

565 self.__UpdateConnectionPreAuthHash(ans.rawData) 

566 

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

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

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

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

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

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

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

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

575 self._Connection['RequireSigning'] = True 

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

577 # Always Sign 

578 self._Connection['RequireSigning'] = True 

579 

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

581 self._Connection['SupportsFileLeasing'] = True 

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

583 self._Connection['SupportsMultiCredit'] = True 

584 

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

586 # Switching to the right packet format 

587 self.SMB_PACKET = SMB3Packet 

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

589 self._Connection['SupportsDirectoryLeasing'] = True 

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

591 self._Connection['SupportsMultiChannel'] = True 

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

593 self._Connection['SupportsPersistentHandles'] = True 

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

595 self._Connection['SupportsEncryption'] = True 

596 

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

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

599 

600 def getCredentials(self): 

601 return ( 

602 self.__userName, 

603 self.__password, 

604 self.__domain, 

605 self.__lmhash, 

606 self.__nthash, 

607 self.__aesKey, 

608 self.__TGT, 

609 self.__TGS) 

610 

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

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

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

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

615 # TGS['sessionKey'] = the sessionKey 

616 # If we have hashes, normalize them 

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

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

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

620 try: # just in case they were converted already 

621 lmhash = a2b_hex(lmhash) 

622 nthash = a2b_hex(nthash) 

623 except: 

624 pass 

625 

626 self.__userName = user 

627 self.__password = password 

628 self.__domain = domain 

629 self.__lmhash = lmhash 

630 self.__nthash = nthash 

631 self.__kdc = kdcHost 

632 self.__aesKey = aesKey 

633 self.__TGT = TGT 

634 self.__TGS = TGS 

635 self._doKerberos= True 

636 

637 sessionSetup = SMB2SessionSetup() 

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

639 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

640 else: 

641 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

642 

643 sessionSetup['Flags'] = 0 

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

645 

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

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

648 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS 

649 from impacket.krb5 import constants 

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

651 from pyasn1.codec.der import decoder, encoder 

652 import datetime 

653 

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

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

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

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

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

659 else: 

660 tgt = TGT['KDC_REP'] 

661 cipher = TGT['cipher'] 

662 sessionKey = TGT['sessionKey'] 

663 

664 # Save the ticket 

665 # If you want, for debugging purposes 

666# from impacket.krb5.ccache import CCache 

667# ccache = CCache() 

668# try: 

669# if TGS is None: 

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

671# else: 

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

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

674# except Exception, e: 

675# print e 

676# pass 

677 

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

679 

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

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

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

683 else: 

684 tgs = TGS['KDC_REP'] 

685 cipher = TGS['cipher'] 

686 sessionKey = TGS['sessionKey'] 

687 

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

689 

690 blob = SPNEGO_NegTokenInit() 

691 

692 # Kerberos 

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

694 

695 # Let's extract the ticket from the TGS 

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

697 ticket = Ticket() 

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

699 

700 # Now let's build the AP_REQ 

701 apReq = AP_REQ() 

702 apReq['pvno'] = 5 

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

704 

705 #Handle mutual authentication 

706 opts = list() 

707 

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

709 from impacket.krb5.constants import APOptions 

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

711 

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

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

714 

715 authenticator = Authenticator() 

716 authenticator['authenticator-vno'] = 5 

717 authenticator['crealm'] = domain 

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

719 now = datetime.datetime.utcnow() 

720 

721 authenticator['cusec'] = now.microsecond 

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

723 

724 encodedAuthenticator = encoder.encode(authenticator) 

725 

726 # Key Usage 11 

727 # AP-REQ Authenticator (includes application authenticator 

728 # subkey), encrypted with the application session key 

729 # (Section 5.5.1) 

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

731 

732 apReq['authenticator'] = noValue 

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

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

735 

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

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

738 

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

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

741 

742 packet = self.SMB_PACKET() 

743 packet['Command'] = SMB2_SESSION_SETUP 

744 packet['Data'] = sessionSetup 

745 

746 #Initiate session preauth hash 

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

748 

749 packetID = self.sendSMB(packet) 

750 ans = self.recvSMB(packetID) 

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

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

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

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

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

756 

757 

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

759 #Lets get the session key in the AP_REP 

760 from impacket.krb5.asn1 import AP_REP, EncAPRepPart 

761 from impacket.krb5.crypto import Key, _enctype_table 

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

763 

764 #in [KILE] 3.1.1.2: 

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

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

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

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

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

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

771 # defined in [RFC4120] section 5.5.1). 

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

773 

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

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

776 

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

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

779 

780 # Key Usage 12 

781 # AP-REP encrypted part (includes application session 

782 # subkey), encrypted with the application session key 

783 # (Section 5.5.2) 

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

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

786 

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

788 

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

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

791 

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

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

794 

795 else: 

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

797 

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

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

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

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

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

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

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

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

806 else: 

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

808 b"SmbSign\x00", 128) 

809 

810 # Do not encrypt anonymous connections 

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

812 self._Connection['SupportsEncryption'] = False 

813 

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

815 self._Session['SigningActivated'] = True 

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

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

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

819 # Application Key 

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

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

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

823 # Encryption Key 

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

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

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

827 # (note the blank space at the end) 

828 # Decryption Key 

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

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

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

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

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

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

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

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

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

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

839 else: 

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

841 b"SmbRpc\x00", 128) 

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

843 b"ServerIn \x00", 128) 

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

845 b"ServerOut\x00", 128) 

846 

847 self._Session['CalculatePreAuthHash'] = False 

848 return True 

849 else: 

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

851 # within the same connection 

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

853 self._Session['Connection'] = 0 

854 self._Session['SessionID'] = 0 

855 self._Session['SigningRequired'] = False 

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

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

858 self._Session['SigningActivated'] = False 

859 self._Session['CalculatePreAuthHash'] = False 

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

861 raise Exception('Unsuccessful Login') 

862 

863 

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

865 # If we have hashes, normalize them 

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

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

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

869 try: # just in case they were converted already 

870 lmhash = a2b_hex(lmhash) 

871 nthash = a2b_hex(nthash) 

872 except: 

873 pass 

874 

875 self.__userName = user 

876 self.__password = password 

877 self.__domain = domain 

878 self.__lmhash = lmhash 

879 self.__nthash = nthash 

880 self.__aesKey = '' 

881 self.__TGT = None 

882 self.__TGS = None 

883 

884 sessionSetup = SMB2SessionSetup() 

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

886 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED 

887 else: 

888 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 

889 

890 sessionSetup['Flags'] = 0 

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

892 

893 # Let's build a NegTokenInit with the NTLMSSP 

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

895 

896 blob = SPNEGO_NegTokenInit() 

897 

898 # NTLMSSP 

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

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

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

902 

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

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

905 

906 # ToDo: 

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

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

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

910 # channel being established. 

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

912 # The PreviousSessionId field MUST be set to zero. 

913 

914 packet = self.SMB_PACKET() 

915 packet['Command'] = SMB2_SESSION_SETUP 

916 packet['Data'] = sessionSetup 

917 

918 packetID = self.sendSMB(packet) 

919 ans = self.recvSMB(packetID) 

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

921 self.__UpdatePreAuthHash (ans.rawData) 

922 

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

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

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

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

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

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

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

930 

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

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

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

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

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

936 try: 

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

938 except: 

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

940 pass 

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

942 try: 

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

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

945 except: 

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

947 pass 

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

949 try: 

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

951 except: 

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

953 pass 

954 

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

956 try: 

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

958 except: 

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

960 pass 

961 

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

963 self.perform_hostname_validation() 

964 

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

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

967 version = ntlmChallenge['Version'] 

968 

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

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

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

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

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

974 

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

976 

977 

978 

979 respToken2 = SPNEGO_NegTokenResp() 

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

981 

982 # Reusing the previous structure 

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

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

985 

986 packetID = self.sendSMB(packet) 

987 packet = self.recvSMB(packetID) 

988 

989 # Let's calculate Key Materials before moving on 

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

991 self._Session['SessionKey'] = exportedSessionKey 

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

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

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

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

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

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

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

999 b"SMBSigningKey\x00", 

1000 self._Session['PreauthIntegrityHashValue'], 

1001 128) 

1002 else: 

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

1004 b"SmbSign\x00", 128) 

1005 try: 

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

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

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

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

1010 

1011 # Do not encrypt anonymous connections 

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

1013 self._Connection['SupportsEncryption'] = False 

1014 

1015 # Calculate the key derivations for dialect 3.0 

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

1017 self._Session['SigningActivated'] = True 

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

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

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

1021 # Application Key 

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

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

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

1025 # Encryption Key 

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

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

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

1029 # (note the blank space at the end) 

1030 # Decryption Key 

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

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

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

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

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

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

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

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

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

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

1041 

1042 else: 

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

1044 b"SmbRpc\x00", 128) 

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

1046 b"ServerIn \x00", 128) 

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

1048 b"ServerOut\x00", 128) 

1049 self._Session['CalculatePreAuthHash'] = False 

1050 return True 

1051 except: 

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

1053 # within the same connection 

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

1055 self._Session['Connection'] = 0 

1056 self._Session['SessionID'] = 0 

1057 self._Session['SigningRequired'] = False 

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

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

1060 self._Session['SigningActivated'] = False 

1061 self._Session['CalculatePreAuthHash'] = False 

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

1063 raise 

1064 

1065 def connectTree(self, share): 

1066 

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

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

1069 

1070 #print self._Session['TreeConnectTable'] 

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

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

1073 # Already connected, no need to reconnect 

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

1075 treeEntry['NumberOfUses'] += 1 

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

1077 return treeEntry['TreeConnectId'] 

1078 

1079 #path = share 

1080 try: 

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

1082 remoteHost = sockaddr[0] 

1083 except: 

1084 remoteHost = self._Connection['ServerIP'] 

1085 path = '\\\\' + remoteHost + '\\' +share 

1086 

1087 treeConnect = SMB2TreeConnect() 

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

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

1090 

1091 packet = self.SMB_PACKET() 

1092 packet['Command'] = SMB2_TREE_CONNECT 

1093 packet['Data'] = treeConnect 

1094 packetID = self.sendSMB(packet) 

1095 packet = self.recvSMB(packetID) 

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

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

1098 treeEntry = copy.deepcopy(TREE_CONNECT) 

1099 treeEntry['ShareName'] = share 

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

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

1102 treeEntry['NumberOfUses'] += 1 

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

1104 treeEntry['IsDfsShare'] = True 

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

1106 treeEntry['IsCAShare'] = True 

1107 

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

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

1110 treeEntry['EncryptData'] = True 

1111 # ToDo: This and what follows 

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

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

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

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

1116 treeEntry['IsScaleoutShare'] = True 

1117 

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

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

1120 

1121 return packet['TreeID'] 

1122 

1123 def disconnectTree(self, treeId): 

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

1125 raise SessionError(STATUS_INVALID_PARAMETER) 

1126 

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

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

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

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

1131 treeEntry['NumberOfUses'] -= 1 

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

1133 return True 

1134 

1135 packet = self.SMB_PACKET() 

1136 packet['Command'] = SMB2_TREE_DISCONNECT 

1137 packet['TreeID'] = treeId 

1138 treeDisconnect = SMB2TreeDisconnect() 

1139 packet['Data'] = treeDisconnect 

1140 packetID = self.sendSMB(packet) 

1141 packet = self.recvSMB(packetID) 

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

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

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

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

1146 filesIDToBeRemoved = [] 

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

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

1149 filesIDToBeRemoved.append(fileID) 

1150 for fileIDToBeRemoved in filesIDToBeRemoved: 

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

1152 return True 

1153 

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

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

1156 raise SessionError(STATUS_INVALID_PARAMETER) 

1157 

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

1159 if len(fileName) > 0: 

1160 fileName = ntpath.normpath(fileName) 

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

1162 fileName = fileName[1:] 

1163 

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

1165 pathName = fileName 

1166 else: 

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

1168 

1169 fileEntry = copy.deepcopy(FILE) 

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

1171 fileEntry['LeaseState'] = SMB2_LEASE_NONE 

1172 self.GlobalFileTable[pathName] = fileEntry 

1173 

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

1175 # Is this file NOT on the root directory? 

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

1177 parentDir = ntpath.dirname(pathName) 

1178 if parentDir in self.GlobalFileTable: 

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

1180 else: 

1181 parentEntry = copy.deepcopy(FILE) 

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

1183 parentEntry['LeaseState'] = SMB2_LEASE_NONE 

1184 self.GlobalFileTable[parentDir] = parentEntry 

1185 

1186 packet = self.SMB_PACKET() 

1187 packet['Command'] = SMB2_CREATE 

1188 packet['TreeID'] = treeId 

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

1190 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS 

1191 

1192 smb2Create = SMB2Create() 

1193 smb2Create['SecurityFlags'] = 0 

1194 smb2Create['RequestedOplockLevel'] = oplockLevel 

1195 smb2Create['ImpersonationLevel'] = impersonationLevel 

1196 smb2Create['DesiredAccess'] = desiredAccess 

1197 smb2Create['FileAttributes'] = fileAttributes 

1198 smb2Create['ShareAccess'] = shareMode 

1199 smb2Create['CreateDisposition'] = creationDisposition 

1200 smb2Create['CreateOptions'] = creationOptions 

1201 

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

1203 if fileName != '': 

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

1205 else: 

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

1207 

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

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

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

1211 

1212 # pad offset to 8-byte align 

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

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

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

1216 

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

1218 smb2Create['Buffer'] += contextsBuf 

1219 else: 

1220 smb2Create['CreateContextsOffset'] = 0 

1221 smb2Create['CreateContextsLength'] = 0 

1222 

1223 packet['Data'] = smb2Create 

1224 

1225 packetID = self.sendSMB(packet) 

1226 ans = self.recvSMB(packetID) 

1227 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1229 

1230 openFile = copy.deepcopy(OPEN) 

1231 openFile['FileID'] = createResponse['FileID'] 

1232 openFile['TreeConnect'] = treeId 

1233 openFile['Oplocklevel'] = oplockLevel 

1234 openFile['Durable'] = False 

1235 openFile['ResilientHandle'] = False 

1236 openFile['LastDisconnectTime'] = 0 

1237 openFile['FileName'] = pathName 

1238 

1239 # ToDo: Complete the OperationBuckets 

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

1241 openFile['DesiredAccess'] = oplockLevel 

1242 openFile['ShareMode'] = oplockLevel 

1243 openFile['CreateOptions'] = oplockLevel 

1244 openFile['FileAttributes'] = oplockLevel 

1245 openFile['CreateDisposition'] = oplockLevel 

1246 

1247 # ToDo: Process the contexts 

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

1249 

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

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

1252 # In our case, str(FileID) 

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

1254 

1255 def close(self, treeId, fileId): 

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

1257 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1259 raise SessionError(STATUS_INVALID_PARAMETER) 

1260 

1261 packet = self.SMB_PACKET() 

1262 packet['Command'] = SMB2_CLOSE 

1263 packet['TreeID'] = treeId 

1264 

1265 smbClose = SMB2Close() 

1266 smbClose['Flags'] = 0 

1267 smbClose['FileID'] = fileId 

1268 

1269 packet['Data'] = smbClose 

1270 

1271 packetID = self.sendSMB(packet) 

1272 ans = self.recvSMB(packetID) 

1273 

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

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

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

1277 

1278 # ToDo Remove stuff from GlobalFileTable 

1279 return True 

1280 

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

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

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

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

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

1286 

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

1288 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1290 raise SessionError(STATUS_INVALID_PARAMETER) 

1291 

1292 packet = self.SMB_PACKET() 

1293 packet['Command'] = SMB2_READ 

1294 packet['TreeID'] = treeId 

1295 

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

1297 maxBytesToRead = self._Connection['MaxReadSize'] 

1298 else: 

1299 maxBytesToRead = bytesToRead 

1300 

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

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

1303 else: 

1304 maxBytesToRead = min(65536,bytesToRead) 

1305 

1306 smbRead = SMB2Read() 

1307 smbRead['Padding'] = 0x50 

1308 smbRead['FileID'] = fileId 

1309 smbRead['Length'] = maxBytesToRead 

1310 smbRead['Offset'] = offset 

1311 packet['Data'] = smbRead 

1312 

1313 packetID = self.sendSMB(packet) 

1314 ans = self.recvSMB(packetID) 

1315 

1316 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1318 retData = readResponse['Buffer'] 

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

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

1321 return retData 

1322 

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

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

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

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

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

1328 

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

1330 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1332 raise SessionError(STATUS_INVALID_PARAMETER) 

1333 

1334 packet = self.SMB_PACKET() 

1335 packet['Command'] = SMB2_WRITE 

1336 packet['TreeID'] = treeId 

1337 

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

1339 maxBytesToWrite = self._Connection['MaxWriteSize'] 

1340 else: 

1341 maxBytesToWrite = bytesToWrite 

1342 

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

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

1345 else: 

1346 maxBytesToWrite = min(65536,bytesToWrite) 

1347 

1348 smbWrite = SMB2Write() 

1349 smbWrite['FileID'] = fileId 

1350 smbWrite['Length'] = maxBytesToWrite 

1351 smbWrite['Offset'] = offset 

1352 smbWrite['WriteChannelInfoOffset'] = 0 

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

1354 packet['Data'] = smbWrite 

1355 

1356 packetID = self.sendSMB(packet) 

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

1358 ans = self.recvSMB(packetID) 

1359 else: 

1360 return maxBytesToWrite 

1361 

1362 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1364 bytesWritten = writeResponse['Count'] 

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

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

1367 return bytesWritten 

1368 

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

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

1371 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1373 raise SessionError(STATUS_INVALID_PARAMETER) 

1374 

1375 packet = self.SMB_PACKET() 

1376 packet['Command'] = SMB2_QUERY_DIRECTORY 

1377 packet['TreeID'] = treeId 

1378 

1379 queryDirectory = SMB2QueryDirectory() 

1380 queryDirectory['FileInformationClass'] = informationClass 

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

1382 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED 

1383 queryDirectory['FileIndex'] = resumeIndex 

1384 queryDirectory['FileID'] = fileId 

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

1386 maxBufferSize = self._Connection['MaxReadSize'] 

1387 queryDirectory['OutputBufferLength'] = maxBufferSize 

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

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

1390 

1391 packet['Data'] = queryDirectory 

1392 

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

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

1395 

1396 packetID = self.sendSMB(packet) 

1397 ans = self.recvSMB(packetID) 

1398 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1400 return queryDirectoryResponse['Buffer'] 

1401 

1402 def echo(self): 

1403 packet = self.SMB_PACKET() 

1404 packet['Command'] = SMB2_ECHO 

1405 smbEcho = SMB2Echo() 

1406 packet['Data'] = smbEcho 

1407 packetID = self.sendSMB(packet) 

1408 ans = self.recvSMB(packetID) 

1409 if ans.isValidAnswer(STATUS_SUCCESS): 

1410 return True 

1411 

1412 def cancel(self, packetID): 

1413 packet = self.SMB_PACKET() 

1414 packet['Command'] = SMB2_CANCEL 

1415 packet['MessageID'] = packetID 

1416 

1417 smbCancel = SMB2Cancel() 

1418 

1419 packet['Data'] = smbCancel 

1420 self.sendSMB(packet) 

1421 

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

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

1424 raise SessionError(STATUS_INVALID_PARAMETER) 

1425 if fileId is None: 

1426 fileId = '\xff'*16 

1427 else: 

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

1429 raise SessionError(STATUS_INVALID_PARAMETER) 

1430 

1431 packet = self.SMB_PACKET() 

1432 packet['Command'] = SMB2_IOCTL 

1433 packet['TreeID'] = treeId 

1434 

1435 smbIoctl = SMB2Ioctl() 

1436 smbIoctl['FileID'] = fileId 

1437 smbIoctl['CtlCode'] = ctlCode 

1438 smbIoctl['MaxInputResponse'] = maxInputResponse 

1439 smbIoctl['MaxOutputResponse'] = maxOutputResponse 

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

1441 if len(inputBlob) == 0: 

1442 smbIoctl['InputOffset'] = 0 

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

1444 else: 

1445 smbIoctl['Buffer'] = inputBlob 

1446 smbIoctl['OutputOffset'] = 0 

1447 smbIoctl['MaxOutputResponse'] = maxOutputResponse 

1448 smbIoctl['Flags'] = flags 

1449 

1450 packet['Data'] = smbIoctl 

1451 

1452 packetID = self.sendSMB(packet) 

1453 

1454 if waitAnswer == 0: 

1455 return True 

1456 

1457 ans = self.recvSMB(packetID) 

1458 

1459 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1461 return smbIoctlResponse['Buffer'] 

1462 

1463 def flush(self,treeId, fileId): 

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

1465 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1467 raise SessionError(STATUS_INVALID_PARAMETER) 

1468 

1469 packet = self.SMB_PACKET() 

1470 packet['Command'] = SMB2_FLUSH 

1471 packet['TreeID'] = treeId 

1472 

1473 smbFlush = SMB2Flush() 

1474 smbFlush['FileID'] = fileId 

1475 

1476 packet['Data'] = smbFlush 

1477 

1478 packetID = self.sendSMB(packet) 

1479 ans = self.recvSMB(packetID) 

1480 

1481 if ans.isValidAnswer(STATUS_SUCCESS): 

1482 return True 

1483 

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

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

1486 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1488 raise SessionError(STATUS_INVALID_PARAMETER) 

1489 

1490 packet = self.SMB_PACKET() 

1491 packet['Command'] = SMB2_LOCK 

1492 packet['TreeID'] = treeId 

1493 

1494 smbLock = SMB2Lock() 

1495 smbLock['FileID'] = fileId 

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

1497 smbLock['LockSequence'] = lockSequence 

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

1499 

1500 packet['Data'] = smbLock 

1501 

1502 packetID = self.sendSMB(packet) 

1503 ans = self.recvSMB(packetID) 

1504 

1505 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1507 return True 

1508 

1509 # ToDo: 

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

1511 # do the following: 

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

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

1514 # returned to the application. 

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

1516 # let BucketNumber = BucketIndex +1. 

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

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

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

1520 # BucketSequence. 

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

1522 

1523 def logoff(self): 

1524 packet = self.SMB_PACKET() 

1525 packet['Command'] = SMB2_LOGOFF 

1526 

1527 smbLogoff = SMB2Logoff() 

1528 

1529 packet['Data'] = smbLogoff 

1530 

1531 packetID = self.sendSMB(packet) 

1532 ans = self.recvSMB(packetID) 

1533 

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

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

1536 # within the same connection 

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

1538 self._Session['Connection'] = 0 

1539 self._Session['SessionID'] = 0 

1540 self._Session['SigningRequired'] = False 

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

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

1543 self._Session['SigningActivated'] = False 

1544 return True 

1545 

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

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

1548 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1550 raise SessionError(STATUS_INVALID_PARAMETER) 

1551 

1552 packet = self.SMB_PACKET() 

1553 packet['Command'] = SMB2_QUERY_INFO 

1554 packet['TreeID'] = treeId 

1555 

1556 queryInfo = SMB2QueryInfo() 

1557 queryInfo['FileID'] = fileId 

1558 queryInfo['InfoType'] = infoType 

1559 queryInfo['FileInfoClass'] = fileInfoClass 

1560 queryInfo['OutputBufferLength'] = 65535 

1561 queryInfo['AdditionalInformation'] = additionalInformation 

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

1563 queryInfo['InputBufferOffset'] = 0 

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

1565 else: 

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

1567 queryInfo['Buffer'] = inputBlob 

1568 queryInfo['Flags'] = flags 

1569 

1570 packet['Data'] = queryInfo 

1571 packetID = self.sendSMB(packet) 

1572 ans = self.recvSMB(packetID) 

1573 

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

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

1576 return queryResponse['Buffer'] 

1577 

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

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

1580 raise SessionError(STATUS_INVALID_PARAMETER) 

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

1582 raise SessionError(STATUS_INVALID_PARAMETER) 

1583 

1584 packet = self.SMB_PACKET() 

1585 packet['Command'] = SMB2_SET_INFO 

1586 packet['TreeID'] = treeId 

1587 

1588 setInfo = SMB2SetInfo() 

1589 setInfo['InfoType'] = infoType 

1590 setInfo['FileInfoClass'] = fileInfoClass 

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

1592 setInfo['AdditionalInformation'] = additionalInformation 

1593 setInfo['FileID'] = fileId 

1594 setInfo['Buffer'] = inputBlob 

1595 

1596 packet['Data'] = setInfo 

1597 packetID = self.sendSMB(packet) 

1598 ans = self.recvSMB(packetID) 

1599 

1600 if ans.isValidAnswer(STATUS_SUCCESS): 

1601 return True 

1602 

1603 def getSessionKey(self): 

1604 if self.getDialect() >= SMB2_DIALECT_30: 

1605 return self._Session['ApplicationKey'] 

1606 else: 

1607 return self._Session['SessionKey'] 

1608 

1609 def setSessionKey(self, key): 

1610 if self.getDialect() >= SMB2_DIALECT_30: 

1611 self._Session['ApplicationKey'] = key 

1612 else: 

1613 self._Session['SessionKey'] = key 

1614 

1615 ###################################################################### 

1616 # Higher level functions 

1617 

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

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

1620 oldPath = ntpath.normpath(oldPath) 

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

1622 oldPath = oldPath[1:] 

1623 

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

1625 newPath = ntpath.normpath(newPath) 

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

1627 newPath = newPath[1:] 

1628 

1629 treeId = self.connectTree(shareName) 

1630 fileId = None 

1631 try: 

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

1633 renameReq = FILE_RENAME_INFORMATION_TYPE_2() 

1634 renameReq['ReplaceIfExists'] = 1 

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

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

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

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

1639 finally: 

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

1641 self.close(treeId, fileId) 

1642 self.disconnectTree(treeId) 

1643 

1644 return True 

1645 

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

1647 finished = False 

1648 writeOffset = offset 

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

1650 if len(data) == 0: 

1651 break 

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

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

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

1655 writeOffset += written 

1656 return writeOffset - offset 

1657 

1658 def isSnapshotRequest(self, path): 

1659 #TODO: use a regex here? 

1660 return '@GMT-' in path 

1661 

1662 def timestampForSnapshot(self, path): 

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

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

1665 from datetime import datetime 

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

1667 fTime *= 10000000 

1668 fTime += 116444736000000000 

1669 

1670 token = SMB2_CREATE_TIMEWARP_TOKEN() 

1671 token['Timestamp'] = fTime 

1672 

1673 ctx = SMB2CreateContext() 

1674 ctx['Next'] = 0 

1675 ctx['NameOffset'] = 16 

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

1677 ctx['DataOffset'] = 24 

1678 ctx['DataLength'] = 8 

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

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

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

1682 

1683 # fix-up the path 

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

1685 if path == '\\': 

1686 path += '*' 

1687 return path, ctx 

1688 

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

1690 createContexts = None 

1691 

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

1693 createContexts = [] 

1694 path, ctx = self.timestampForSnapshot(path) 

1695 createContexts.append(ctx) 

1696 

1697 # ToDo: Handle situations where share is password protected 

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

1699 path = ntpath.normpath(path) 

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

1701 path = path[1:] 

1702 

1703 treeId = self.connectTree(shareName) 

1704 

1705 fileId = None 

1706 try: 

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

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

1709 FILE_SHARE_WRITE | FILE_SHARE_DELETE, 

1710 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0, 

1711 createContexts=createContexts) 

1712 res = '' 

1713 files = [] 

1714 from impacket import smb 

1715 while True: 

1716 try: 

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

1718 informationClass=FILE_FULL_DIRECTORY_INFORMATION) 

1719 nextOffset = 1 

1720 while nextOffset != 0: 

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

1722 fileInfo.fromString(res) 

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

1724 fileInfo['LastChangeTime'], fileInfo['EndOfFile'], 

1725 fileInfo['AllocationSize'], fileInfo['ExtFileAttributes'], 

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

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

1728 nextOffset = fileInfo['NextEntryOffset'] 

1729 res = res[nextOffset:] 

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

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

1732 raise 

1733 break 

1734 except Exception as e: 

1735 print(str(e)) 

1736 raise 

1737 finally: 

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

1739 self.close(treeId, fileId) 

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

1741 

1742 return files 

1743 

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

1745 # ToDo: Handle situations where share is password protected 

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

1747 pathName = ntpath.normpath(pathName) 

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

1749 pathName = pathName[1:] 

1750 

1751 treeId = self.connectTree(shareName) 

1752 

1753 fileId = None 

1754 try: 

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

1756 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0) 

1757 finally: 

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

1759 self.close(treeId, fileId) 

1760 self.disconnectTree(treeId) 

1761 

1762 return True 

1763 

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

1765 # ToDo: Handle situations where share is password protected 

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

1767 pathName = ntpath.normpath(pathName) 

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

1769 pathName = pathName[1:] 

1770 

1771 treeId = self.connectTree(shareName) 

1772 

1773 fileId = None 

1774 try: 

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

1776 shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 

1777 creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, 

1778 creationDisposition=FILE_OPEN, fileAttributes=0) 

1779 from impacket import smb 

1780 delete_req = smb.SMBSetFileDispositionInfo() 

1781 delete_req['DeletePending'] = True 

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

1783 finally: 

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

1785 self.close(treeId, fileId) 

1786 self.disconnectTree(treeId) 

1787 

1788 return True 

1789 

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

1791 # ToDo: Handle situations where share is password protected 

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

1793 pathName = ntpath.normpath(pathName) 

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

1795 pathName = pathName[1:] 

1796 

1797 treeId = self.connectTree(shareName) 

1798 

1799 fileId = None 

1800 try: 

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

1802 finally: 

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

1804 self.close(treeId, fileId) 

1805 self.disconnectTree(treeId) 

1806 

1807 return True 

1808 

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

1810 createContexts = None 

1811 

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

1813 createContexts = [] 

1814 path, ctx = self.timestampForSnapshot(path) 

1815 createContexts.append(ctx) 

1816 

1817 # ToDo: Handle situations where share is password protected 

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

1819 path = ntpath.normpath(path) 

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

1821 path = path[1:] 

1822 

1823 treeId = self.connectTree(shareName) 

1824 fileId = None 

1825 from impacket import smb 

1826 try: 

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

1828 res = self.queryInfo(treeId, fileId) 

1829 fileInfo = smb.SMBQueryFileStandardInfo(res) 

1830 fileSize = fileInfo['EndOfFile'] 

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

1832 # Skip reading 0 bytes files. 

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

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

1835 callback(data) 

1836 else: 

1837 written = 0 

1838 toBeRead = fileSize-offset 

1839 while written < toBeRead: 

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

1841 written += len(data) 

1842 offset += len(data) 

1843 callback(data) 

1844 finally: 

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

1846 self.close(treeId, fileId) 

1847 self.disconnectTree(treeId) 

1848 

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

1850 # ToDo: Handle situations where share is password protected 

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

1852 path = ntpath.normpath(path) 

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

1854 path = path[1:] 

1855 

1856 treeId = self.connectTree(shareName) 

1857 fileId = None 

1858 try: 

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

1860 finished = False 

1861 writeOffset = offset 

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

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

1864 if len(data) == 0: 

1865 break 

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

1867 writeOffset += written 

1868 finally: 

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

1870 self.close(treeId, fileId) 

1871 self.disconnectTree(treeId) 

1872 

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

1874 pipename = ntpath.basename(pipename) 

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

1876 raise SessionError(STATUS_INVALID_PARAMETER) 

1877 if len(pipename) > 0xffff: 

1878 raise SessionError(STATUS_INVALID_PARAMETER) 

1879 

1880 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE() 

1881 pipeWait['Timeout'] = timeout*100000 

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

1883 pipeWait['TimeoutSpecified'] = 1 

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

1885 

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

1887 

1888 def getIOCapabilities(self): 

1889 res = dict() 

1890 

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

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

1893 return res 

1894 

1895 

1896 ###################################################################### 

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

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

1899 # when implementing new client calls. 

1900 get_server_name = getServerName 

1901 get_client_name = getClientName 

1902 get_server_domain = getServerDomain 

1903 get_server_dns_domain_name = getServerDNSDomainName 

1904 get_server_dns_host_name = getServerDNSHostName 

1905 get_remote_name = getRemoteName 

1906 set_remote_name = setRemoteName 

1907 get_remote_host = getServerIP 

1908 get_server_os = getServerOS 

1909 get_server_os_major = getServerOSMajor 

1910 get_server_os_minor = getServerOSMinor 

1911 get_server_os_build = getServerOSBuild 

1912 tree_connect_andx = connectTree 

1913 tree_connect = connectTree 

1914 connect_tree = connectTree 

1915 disconnect_tree = disconnectTree 

1916 set_timeout = setTimeout 

1917 use_timeout = useTimeout 

1918 stor_file = storeFile 

1919 retr_file = retrieveFile 

1920 list_path = listPath 

1921 

1922 def close_session(self): 

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

1924 self._NetBIOSSession.close() 

1925 self._NetBIOSSession = None 

1926 

1927 def doesSupportNTLMv2(self): 

1928 # Always true :P 

1929 return True 

1930 

1931 def is_login_required(self): 

1932 # Always true :P 

1933 return True 

1934 

1935 def is_signing_required(self): 

1936 return self._Connection['RequireSigning'] 

1937 

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

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

1940 fileName = fileName[1:] 

1941 

1942 if cmd is not None: 

1943 from impacket import smb 

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

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

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

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

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

1949 

1950 else: 

1951 return self.create(treeId, fileName, 

1952 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | 

1953 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL, 

1954 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 ) 

1955 

1956 def get_socket(self): 

1957 return self._NetBIOSSession.get_socket() 

1958 

1959 

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

1961 # ToDo: Handle the custom smb_packet situation 

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

1963 

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

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

1966 

1967 def TransactNamedPipeRecv(self): 

1968 ans = self.recvSMB() 

1969 

1970 if ans.isValidAnswer(STATUS_SUCCESS): 

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

1972 return smbIoctlResponse['Buffer'] 

1973 

1974 

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

1976 # ToDo: Handle the custom smb_packet situation 

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

1978 max_size = self._Connection['MaxReadSize'] 

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

1980 

1981 def list_shared(self): 

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

1983 raise SessionError(STATUS_NOT_IMPLEMENTED) 

1984 

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

1986 # ToDo Return all the attributes of the file 

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

1988 fileName = fileName[1:] 

1989 

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

1991 return fileId, 0, 0, 0, 0, 0, 0, 0, 0 

1992 

1993 def set_session_key(self, signingKey): 

1994 self._Session['SessionKey'] = signingKey 

1995 self._Session['SigningActivated'] = True 

1996 self._Session['SigningRequired'] = True 

1997 

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

1999 self._strict_hostname_validation = validate 

2000 self._validation_allow_absent = accept_empty 

2001 self._accepted_hostname = hostname 

2002 

2003 def perform_hostname_validation(self): 

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

2005 if not self._validation_allow_absent: 

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

2007 return 

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

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

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