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

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

## 1238 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

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

2#

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

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

5# for more information.

6#

7# Author: Alberto Solino (@agsolino)

8#

9# Description:

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

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

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

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

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

15# it certainly helps following the document way easier.

16#

17# ToDo:

18# [X] Implement SMB2_CHANGE_NOTIFY

19# [X] Implement SMB2_QUERY_INFO

20# [X] Implement SMB2_SET_INFO

21# [ ] Implement SMB2_OPLOCK_BREAK

22# [X] Implement SMB3 signing

23# [X] Implement SMB3 encryption

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

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

26#

27 from __future__ import division

28 from __future__ import print_function

29

30 import socket

31 import ntpath

32 import random

33 import string

34 import struct

35 from six import indexbytes, b

36 from binascii import a2b_hex

37 from contextlib import contextmanager

38 from pyasn1.type.univ import noValue

39 from Cryptodome.Cipher import AES

40

41 from impacket import nmb, ntlm, uuid, crypto

42 from impacket.smb3structs import *

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

44 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES

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

46 from impacket.krb5.gssapi import KRB5_AP_REQ

47

48

49# For signing

50 import hashlib, hmac, copy

51

52# Our random number generator

53 try:

54 rand = random.SystemRandom()

55 except NotImplementedError:

56 rand = random

57 pass

58

59# Structs to be used

60 TREE_CONNECT = {

61 'ShareName' : '',

62 'TreeConnectId' : 0,

63 'Session' : 0,

64 'IsDfsShare' : False,

65 # If the client implements the SMB 3.0 dialect,

66 # the client MUST also implement the following

67 'IsCAShare' : False,

68 'EncryptData' : False,

69 'IsScaleoutShare' : False,

70 # Outside the protocol

71 'NumberOfUses' : 0,

72}

73

74 FILE = {

75 'OpenTable' : [],

76 'LeaseKey' : '',

77 'LeaseState' : 0,

78 'LeaseEpoch' : 0,

79}

80

81 OPEN = {

82 'FileID' : '',

83 'TreeConnect' : 0,

84 'Connection' : 0, # Not Used

85 'Oplocklevel' : 0,

86 'Durable' : False,

87 'FileName' : '',

88 'ResilientHandle' : False,

89 'LastDisconnectTime' : 0,

90 'ResilientTimeout' : 0,

91 'OperationBuckets' : [],

92 # If the client implements the SMB 3.0 dialect,

93 # the client MUST implement the following

94 'CreateGuid' : '',

95 'IsPersistent' : False,

96 'DesiredAccess' : '',

97 'ShareMode' : 0,

98 'CreateOption' : '',

99 'FileAttributes' : '',

100 'CreateDisposition' : '',

101}

102

103 REQUEST = {

104 'CancelID' : '',

105 'Message' : '',

106 'Timestamp' : 0,

107}

108

109 CHANNEL = {

110 'SigningKey' : '',

111 'Connection' : 0,

112}

113

114

115 class SessionError(Exception):

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

117 Exception.__init__(self)

118 self.error = error

119 self.packet = packet

120

121 def get_error_code( self ):

122 return self.error

123

124 def get_error_packet( self ):

125 return self.packet

126

127 def __str__( self ):

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

129

130

131 class SMB3:

132 class HostnameValidationException(Exception):

133 pass

134

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

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

137

138 # [MS-SMB2] Section 3

139 self.RequireMessageSigning = False #

140 self.ConnectionTable = {}

141 self.GlobalFileTable = {}

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

143 # Only for SMB 3.0

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

145 self.MaxDialect = []

146 self.RequireSecureNegotiate = False

147

148 # Per Transport Connection Data

149 self._Connection = {

150 # Indexed by SessionID

151 #'SessionTable' : {},

152 # Indexed by MessageID

153 'OutstandingRequests' : {},

154 'OutstandingResponses' : {}, #

155 'SequenceWindow' : 0, #

156 'GSSNegotiateToken' : '', #

157 'MaxTransactSize' : 0, #

158 'MaxReadSize' : 0, #

159 'MaxWriteSize' : 0, #

160 'ServerGuid' : '', #

161 'RequireSigning' : False, #

162 'ServerName' : '', #

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

164 # also implement the following

165 'Dialect' : 0, #

166 'SupportsFileLeasing' : False, #

167 'SupportsMultiCredit' : False, #

168 # If the client implements the SMB 3.0 dialect,

169 # it MUST also implement the following

170 'SupportsDirectoryLeasing' : False, #

171 'SupportsMultiChannel' : False, #

172 'SupportsPersistentHandles': False, #

173 'SupportsEncryption' : False, #

174 'ClientCapabilities' : 0,

175 'ServerCapabilities' : 0, #

176 'ClientSecurityMode' : 0, #

177 'ServerSecurityMode' : 0, #

178 # Outside the protocol

179 'ServerIP' : '', #

180 'ClientName' : '', #

181 #GSSoptions (MutualAuth and Delegate)

182 'GSSoptions' : {},

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

184 }

185

186 self._Session = {

187 'SessionID' : 0, #

188 'TreeConnectTable' : {}, #

189 'SessionKey' : b'', #

190 'SigningRequired' : False, #

191 'Connection' : 0, #

192 'UserCredentials' : '', #

193 'OpenTable' : {}, #

194 # If the client implements the SMB 3.0 dialect,

195 # it MUST also implement the following

196 'ChannelList' : [],

197 'ChannelSequence' : 0,

198 #'EncryptData' : False,

199 'EncryptData' : True,

200 'EncryptionKey' : '',

201 'DecryptionKey' : '',

202 'SigningKey' : '',

203 'ApplicationKey' : b'',

204 # Outside the protocol

205 'SessionFlags' : 0, #

206 'ServerName' : '', #

207 'ServerDomain' : '', #

208 'ServerDNSDomainName' : '', #

209 'ServerDNSHostName' : '', #

210 'ServerOS' : '', #

211 'SigningActivated' : False, #

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

213 'CalculatePreAuthHash' : True,

214 }

215

216 self.SMB_PACKET = SMB2Packet

217

218 self._timeout = timeout

219 self._Connection['ServerIP'] = remote_host

220 self._NetBIOSSession = None

221 self._preferredDialect = preferredDialect

222 self._doKerberos = False

223

224 # Strict host validation - off by default

225 self._strict_hostname_validation = False

226 self._validation_allow_absent = True

227 self._accepted_hostname = ''

228

229 self.__userName = ''

230 self.__password = ''

231 self.__domain = ''

232 self.__lmhash = ''

233 self.__nthash = ''

234 self.__kdc = ''

235 self.__aesKey = ''

236 self.__TGT = None

237 self.__TGS = None

238

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

240 self._Connection['ServerName'] = remote_host

241 else:

242 self._Connection['ServerName'] = remote_name

243

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

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

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

247 else:

248 self._Connection['ClientName'] = my_name

249

250 if session is None:

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

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

253 my_name = socket.gethostname()

254 i = my_name.find('.')

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

256 my_name = my_name[:i]

257

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

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

260 else:

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

262

263 self.negotiateSession(preferredDialect)

264 else:

265 self._NetBIOSSession = session

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

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

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

269 self.negotiateSession(preferredDialect, negSessionResponse)

270

271 def printStatus(self):

272 print("CONNECTION")

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

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

275 print()

276 print("SESSION")

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

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

279

280 def __UpdateConnectionPreAuthHash(self, data):

281 from Cryptodome.Hash import SHA512

282 calculatedHash = SHA512.new()

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

284 calculatedHash.update(data)

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

286

287 def __UpdatePreAuthHash(self, data):

288 from Cryptodome.Hash import SHA512

289 calculatedHash = SHA512.new()

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

291 calculatedHash.update(data)

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

293

294 def getKerberos(self):

295 return self._doKerberos

296

297 def getServerName(self):

298 return self._Session['ServerName']

299

300 def getClientName(self):

301 return self._Session['ClientName']

302

303 def getRemoteName(self):

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

305 return self._Connection['ServerName']

306 return self._Session['ServerName']

307

308 def setRemoteName(self, name):

309 self._Session['ServerName'] = name

310 return True

311

312 def getServerIP(self):

313 return self._Connection['ServerIP']

314

315 def getServerDomain(self):

316 return self._Session['ServerDomain']

317

318 def getServerDNSDomainName(self):

319 return self._Session['ServerDNSDomainName']

320

321 def getServerDNSHostName(self):

322 return self._Session['ServerDNSHostName']

323

324 def getServerOS(self):

325 return self._Session['ServerOS']

326

327 def getServerOSMajor(self):

328 return self._Session['ServerOSMajor']

329

330 def getServerOSMinor(self):

331 return self._Session['ServerOSMinor']

332

333 def getServerOSBuild(self):

334 return self._Session['ServerOSBuild']

335

336 def isGuestSession(self):

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

338

339 def setTimeout(self, timeout):

340 self._timeout = timeout

341

342 @contextmanager

343 def useTimeout(self, timeout):

344 prev_timeout = self.getTimeout(timeout)

345 try:

346 yield

347 finally:

348 self.setTimeout(prev_timeout)

349

350 def getDialect(self):

351 return self._Connection['Dialect']

352

353 def signSMB(self, packet):

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

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

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

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

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

359 else:

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

361 p = packet.getData()

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

363 packet['Signature'] = signature

364

365 def sendSMB(self, packet):

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

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

368

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

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

371 # SMB2 header to Session.ChannelSequence

372

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

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

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

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

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

378

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

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

381 packet['CreditCharge'] = 1

382

383 # Standard credit request after negotiating protocol

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

385 packet['CreditRequestResponse'] = 127

386

387 messageId = packet['MessageID']

388

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

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

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

392 packet['Flags'] = SMB2_FLAGS_SIGNED

393 self.signSMB(packet)

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

395 packet['Flags'] = SMB2_FLAGS_SIGNED

396 self.signSMB(packet)

397

398 if packet['Command'] is SMB2_NEGOTIATE:

399 data = packet.getData()

400 self.__UpdateConnectionPreAuthHash(data)

401 self._Session['CalculatePreAuthHash'] = False

402

403 if packet['Command'] is SMB2_SESSION_SETUP:

404 self._Session['CalculatePreAuthHash'] = True

405

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

407 plainText = packet.getData()

408 transformHeader = SMB2_TRANSFORM_HEADER()

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

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

411 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM

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

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

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

415 cipherText = cipher.encrypt(plainText)

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

417 packet = transformHeader.getData() + cipherText

418

419 self._NetBIOSSession.send_packet(packet)

420 else:

421 data = packet.getData()

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

423 self.__UpdatePreAuthHash(data)

424

425 self._NetBIOSSession.send_packet(data)

426

427 return messageId

428

429 def recvSMB(self, packetID = None):

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

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

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

433

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

435

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

437 # Packet is encrypted

438 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())

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

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

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

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

443 packet = SMB2Packet(plainText)

444 else:

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

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

447 # see [MS-ERREF] section 2.3.

448 packet = SMB2Packet(data.get_trailer())

449

450 # Loop while we receive pending requests

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

452 status = STATUS_PENDING

453 while status == STATUS_PENDING:

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

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

456 packet = SMB2Packet(data.get_trailer())

457 else:

458 # Packet is encrypted

459 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())

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

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

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

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

464 packet = SMB2Packet(plainText)

465 status = packet['Status']

466

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

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

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

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

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

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

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

474 return packet

475 else:

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

477 return self.recvSMB(packetID)

478

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

480 # Let's store some data for later use

481 self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

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

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

484 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION

485 currentDialect = SMB2_DIALECT_WILDCARD

486

487 # Do we have a negSessionPacket already?

488 if negSessionResponse is not None:

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

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

491 currentDialect = negResp['DialectRevision']

492

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

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

495

496 packet = self.SMB_PACKET()

497 packet['Command'] = SMB2_NEGOTIATE

498 negSession = SMB2Negotiate()

499

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

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

502 negSession['ClientGuid'] = self.ClientGuid

503 if preferredDialect is not None:

504 negSession['Dialects'] = [preferredDialect]

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

506 # Build the Contexts

507 contextData = SMB311ContextData()

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

509 contextData['NegotiateContextCount'] = 0

510 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_PREAUTH_INTEGRITY_CAPABILITIES

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

512 negotiateContext = SMB2NegotiateContext()

513 negotiateContext['ContextType'] = SMB2_PREAUTH_INTEGRITY_CAPABILITIES

514

515 preAuthIntegrityCapabilities = SMB2PreAuthIntegrityCapabilities()

516 preAuthIntegrityCapabilities['HashAlgorithmCount'] = 1

517 preAuthIntegrityCapabilities['SaltLength'] = 32

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

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

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

521

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

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

524 contextData['NegotiateContextCount'] += 1

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

526

527 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_ENCRYPTION_CAPABILITIES

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

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

530

531 negotiateContext2 = SMB2NegotiateContext ()

532 negotiateContext2['ContextType'] = SMB2_ENCRYPTION_CAPABILITIES

533

534 encryptionCapabilities = SMB2EncryptionCapabilities()

535 encryptionCapabilities['CipherCount'] = 1

536 encryptionCapabilities['Ciphers'] = 1

537

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

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

540 contextData['NegotiateContextCount'] += 1

541

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

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

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

545 # previous negotiate context.

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

547

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

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

550

551 else:

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

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

554 packet['Data'] = negSession

555

556 packetID = self.sendSMB(packet)

557 ans = self.recvSMB(packetID)

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

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

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

561 self.__UpdateConnectionPreAuthHash(ans.rawData)

562

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

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

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

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

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

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

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

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

571 self._Connection['RequireSigning'] = True

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

573 # Always Sign

574 self._Connection['RequireSigning'] = True

575

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

577 self._Connection['SupportsFileLeasing'] = True

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

579 self._Connection['SupportsMultiCredit'] = True

580

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

582 # Switching to the right packet format

583 self.SMB_PACKET = SMB3Packet

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

585 self._Connection['SupportsDirectoryLeasing'] = True

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

587 self._Connection['SupportsMultiChannel'] = True

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

589 self._Connection['SupportsPersistentHandles'] = True

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

591 self._Connection['SupportsEncryption'] = True

592

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

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

595

596 def getCredentials(self):

597 return (

598 self.__userName,

599 self.__password,

600 self.__domain,

601 self.__lmhash,

602 self.__nthash,

603 self.__aesKey,

604 self.__TGT,

605 self.__TGS)

606

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

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

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

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

611 # TGS['sessionKey'] = the sessionKey

612 # If we have hashes, normalize them

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

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

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

616 try: # just in case they were converted already

617 lmhash = a2b_hex(lmhash)

618 nthash = a2b_hex(nthash)

619 except:

620 pass

621

622 self.__userName = user

623 self.__password = password

624 self.__domain = domain

625 self.__lmhash = lmhash

626 self.__nthash = nthash

627 self.__kdc = kdcHost

628 self.__aesKey = aesKey

629 self.__TGT = TGT

630 self.__TGS = TGS

631 self._doKerberos= True

632

633 sessionSetup = SMB2SessionSetup()

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

635 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED

636 else:

637 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

638

639 sessionSetup['Flags'] = 0

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

641

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

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

644 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS

645 from impacket.krb5 import constants

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

647 from pyasn1.codec.der import decoder, encoder

648 import datetime

649

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

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

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

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

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

655 else:

656 tgt = TGT['KDC_REP']

657 cipher = TGT['cipher']

658 sessionKey = TGT['sessionKey']

659

660 # Save the ticket

661 # If you want, for debugging purposes

662# from impacket.krb5.ccache import CCache

663# ccache = CCache()

664# try:

665# if TGS is None:

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

667# else:

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

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

670# except Exception, e:

671# print e

672# pass

673

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

675

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

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

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

679 else:

680 tgs = TGS['KDC_REP']

681 cipher = TGS['cipher']

682 sessionKey = TGS['sessionKey']

683

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

685

686 blob = SPNEGO_NegTokenInit()

687

688 # Kerberos

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

690

691 # Let's extract the ticket from the TGS

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

693 ticket = Ticket()

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

695

696 # Now let's build the AP_REQ

697 apReq = AP_REQ()

698 apReq['pvno'] = 5

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

700

701 #Handle mutual authentication

702 opts = list()

703

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

705 from impacket.krb5.constants import APOptions

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

707

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

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

710

711 authenticator = Authenticator()

712 authenticator['authenticator-vno'] = 5

713 authenticator['crealm'] = domain

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

715 now = datetime.datetime.utcnow()

716

717 authenticator['cusec'] = now.microsecond

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

719

720 encodedAuthenticator = encoder.encode(authenticator)

721

722 # Key Usage 11

723 # AP-REQ Authenticator (includes application authenticator

724 # subkey), encrypted with the application session key

725 # (Section 5.5.1)

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

727

728 apReq['authenticator'] = noValue

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

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

731

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

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

734

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

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

737

738 packet = self.SMB_PACKET()

739 packet['Command'] = SMB2_SESSION_SETUP

740 packet['Data'] = sessionSetup

741

742 #Initiate session preauth hash

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

744

745 packetID = self.sendSMB(packet)

746 ans = self.recvSMB(packetID)

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

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

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

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

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

752

753

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

755 #Lets get the session key in the AP_REP

756 from impacket.krb5.asn1 import AP_REP, EncAPRepPart

757 from impacket.krb5.crypto import Key, _enctype_table

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

759

760 #in [KILE] 3.1.1.2:

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

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

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

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

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

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

767 # defined in [RFC4120] section 5.5.1).

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

769

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

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

772

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

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

775

776 # Key Usage 12

777 # AP-REP encrypted part (includes application session

778 # subkey), encrypted with the application session key

779 # (Section 5.5.2)

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

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

782

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

784

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

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

787

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

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

790

791 else:

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

793

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

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

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

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

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

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

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

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

802 else:

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

804 b"SmbSign\x00", 128)

805

806 # Do not encrypt anonymous connections

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

808 self._Connection['SupportsEncryption'] = False

809

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

811 self._Session['SigningActivated'] = True

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

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

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

815 # Application Key

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

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

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

819 # Encryption Key

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

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

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

823 # (note the blank space at the end)

824 # Decryption Key

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

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

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

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

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

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

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

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

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

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

835 else:

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

837 b"SmbRpc\x00", 128)

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

839 b"ServerIn \x00", 128)

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

841 b"ServerOut\x00", 128)

842

843 self._Session['CalculatePreAuthHash'] = False

844 return True

845 else:

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

847 # within the same connection

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

849 self._Session['Connection'] = 0

850 self._Session['SessionID'] = 0

851 self._Session['SigningRequired'] = False

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

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

854 self._Session['SigningActivated'] = False

855 self._Session['CalculatePreAuthHash'] = False

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

857 raise Exception('Unsuccessful Login')

858

859

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

861 # If we have hashes, normalize them

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

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

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

865 try: # just in case they were converted already

866 lmhash = a2b_hex(lmhash)

867 nthash = a2b_hex(nthash)

868 except:

869 pass

870

871 self.__userName = user

872 self.__password = password

873 self.__domain = domain

874 self.__lmhash = lmhash

875 self.__nthash = nthash

876 self.__aesKey = ''

877 self.__TGT = None

878 self.__TGS = None

879

880 sessionSetup = SMB2SessionSetup()

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

882 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED

883 else:

884 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

885

886 sessionSetup['Flags'] = 0

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

888

889 # Let's build a NegTokenInit with the NTLMSSP

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

891

892 blob = SPNEGO_NegTokenInit()

893

894 # NTLMSSP

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

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

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

898

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

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

901

902 # ToDo:

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

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

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

906 # channel being established.

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

908 # The PreviousSessionId field MUST be set to zero.

909

910 packet = self.SMB_PACKET()

911 packet['Command'] = SMB2_SESSION_SETUP

912 packet['Data'] = sessionSetup

913

914 packetID = self.sendSMB(packet)

915 ans = self.recvSMB(packetID)

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

917 self.__UpdatePreAuthHash (ans.rawData)

918

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

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

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

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

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

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

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

926

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

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

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

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

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

932 try:

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

934 except:

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

936 pass

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

938 try:

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

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

941 except:

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

943 pass

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

945 try:

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

947 except:

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

949 pass

950

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

952 try:

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

954 except:

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

956 pass

957

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

959 self.perform_hostname_validation()

960

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

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

963 version = ntlmChallenge['Version']

964

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

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

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

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

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

970

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

972

973

974

975 respToken2 = SPNEGO_NegTokenResp()

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

977

978 # Reusing the previous structure

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

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

981

982 packetID = self.sendSMB(packet)

983 packet = self.recvSMB(packetID)

984

985 # Let's calculate Key Materials before moving on

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

987 self._Session['SessionKey'] = exportedSessionKey

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

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

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

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

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

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

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

995 b"SMBSigningKey\x00",

996 self._Session['PreauthIntegrityHashValue'],

997 128)

998 else:

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

1000 b"SmbSign\x00", 128)

1001 try:

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

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

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

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

1006

1007 # Do not encrypt anonymous connections

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

1009 self._Connection['SupportsEncryption'] = False

1010

1011 # Calculate the key derivations for dialect 3.0

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

1013 self._Session['SigningActivated'] = True

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

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

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

1017 # Application Key

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

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

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

1021 # Encryption Key

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

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

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

1025 # (note the blank space at the end)

1026 # Decryption Key

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

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

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

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

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

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

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

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

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

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

1037

1038 else:

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

1040 b"SmbRpc\x00", 128)

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

1042 b"ServerIn \x00", 128)

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

1044 b"ServerOut\x00", 128)

1045 self._Session['CalculatePreAuthHash'] = False

1046 return True

1047 except:

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

1049 # within the same connection

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

1051 self._Session['Connection'] = 0

1052 self._Session['SessionID'] = 0

1053 self._Session['SigningRequired'] = False

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

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

1056 self._Session['SigningActivated'] = False

1057 self._Session['CalculatePreAuthHash'] = False

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

1059 raise

1060

1061 def connectTree(self, share):

1062

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

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

1065

1066 #print self._Session['TreeConnectTable']

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

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

1069 # Already connected, no need to reconnect

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

1071 treeEntry['NumberOfUses'] += 1

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

1073 return treeEntry['TreeConnectId']

1074

1075 #path = share

1076 try:

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

1078 remoteHost = sockaddr[0]

1079 except:

1080 remoteHost = self._Connection['ServerIP']

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

1082

1083 treeConnect = SMB2TreeConnect()

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

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

1086

1087 packet = self.SMB_PACKET()

1088 packet['Command'] = SMB2_TREE_CONNECT

1089 packet['Data'] = treeConnect

1090 packetID = self.sendSMB(packet)

1091 packet = self.recvSMB(packetID)

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

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

1094 treeEntry = copy.deepcopy(TREE_CONNECT)

1095 treeEntry['ShareName'] = share

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

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

1098 treeEntry['NumberOfUses'] += 1

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

1100 treeEntry['IsDfsShare'] = True

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

1102 treeEntry['IsCAShare'] = True

1103

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

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

1106 treeEntry['EncryptData'] = True

1107 # ToDo: This and what follows

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

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

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

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

1112 treeEntry['IsScaleoutShare'] = True

1113

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

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

1116

1117 return packet['TreeID']

1118

1119 def disconnectTree(self, treeId):

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

1121 raise SessionError(STATUS_INVALID_PARAMETER)

1122

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

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

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

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

1127 treeEntry['NumberOfUses'] -= 1

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

1129 return True

1130

1131 packet = self.SMB_PACKET()

1132 packet['Command'] = SMB2_TREE_DISCONNECT

1133 packet['TreeID'] = treeId

1134 treeDisconnect = SMB2TreeDisconnect()

1135 packet['Data'] = treeDisconnect

1136 packetID = self.sendSMB(packet)

1137 packet = self.recvSMB(packetID)

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

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

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

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

1142 filesIDToBeRemoved = []

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

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

1145 filesIDToBeRemoved.append(fileID)

1146 for fileIDToBeRemoved in filesIDToBeRemoved:

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

1148 return True

1149

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

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

1152 raise SessionError(STATUS_INVALID_PARAMETER)

1153

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

1155 if len(fileName) > 0:

1156 fileName = ntpath.normpath(fileName)

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

1158 fileName = fileName[1:]

1159

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

1161 pathName = fileName

1162 else:

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

1164

1165 fileEntry = copy.deepcopy(FILE)

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

1167 fileEntry['LeaseState'] = SMB2_LEASE_NONE

1168 self.GlobalFileTable[pathName] = fileEntry

1169

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

1171 # Is this file NOT on the root directory?

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

1173 parentDir = ntpath.dirname(pathName)

1174 if parentDir in self.GlobalFileTable:

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

1176 else:

1177 parentEntry = copy.deepcopy(FILE)

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

1179 parentEntry['LeaseState'] = SMB2_LEASE_NONE

1180 self.GlobalFileTable[parentDir] = parentEntry

1181

1182 packet = self.SMB_PACKET()

1183 packet['Command'] = SMB2_CREATE

1184 packet['TreeID'] = treeId

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

1186 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS

1187

1188 smb2Create = SMB2Create()

1189 smb2Create['SecurityFlags'] = 0

1190 smb2Create['RequestedOplockLevel'] = oplockLevel

1191 smb2Create['ImpersonationLevel'] = impersonationLevel

1192 smb2Create['DesiredAccess'] = desiredAccess

1193 smb2Create['FileAttributes'] = fileAttributes

1194 smb2Create['ShareAccess'] = shareMode

1195 smb2Create['CreateDisposition'] = creationDisposition

1196 smb2Create['CreateOptions'] = creationOptions

1197

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

1199 if fileName != '':

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

1201 else:

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

1203

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

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

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

1207

1208 # pad offset to 8-byte align

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

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

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

1212

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

1214 smb2Create['Buffer'] += contextsBuf

1215 else:

1216 smb2Create['CreateContextsOffset'] = 0

1217 smb2Create['CreateContextsLength'] = 0

1218

1219 packet['Data'] = smb2Create

1220

1221 packetID = self.sendSMB(packet)

1222 ans = self.recvSMB(packetID)

1223 if ans.isValidAnswer(STATUS_SUCCESS):

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

1225

1226 openFile = copy.deepcopy(OPEN)

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

1228 openFile['TreeConnect'] = treeId

1229 openFile['Oplocklevel'] = oplockLevel

1230 openFile['Durable'] = False

1231 openFile['ResilientHandle'] = False

1232 openFile['LastDisconnectTime'] = 0

1233 openFile['FileName'] = pathName

1234

1235 # ToDo: Complete the OperationBuckets

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

1237 openFile['DesiredAccess'] = oplockLevel

1238 openFile['ShareMode'] = oplockLevel

1239 openFile['CreateOptions'] = oplockLevel

1240 openFile['FileAttributes'] = oplockLevel

1241 openFile['CreateDisposition'] = oplockLevel

1242

1243 # ToDo: Process the contexts

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

1245

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

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

1248 # In our case, str(FileID)

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

1250

1251 def close(self, treeId, fileId):

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

1253 raise SessionError(STATUS_INVALID_PARAMETER)

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

1255 raise SessionError(STATUS_INVALID_PARAMETER)

1256

1257 packet = self.SMB_PACKET()

1258 packet['Command'] = SMB2_CLOSE

1259 packet['TreeID'] = treeId

1260

1261 smbClose = SMB2Close()

1262 smbClose['Flags'] = 0

1263 smbClose['FileID'] = fileId

1264

1265 packet['Data'] = smbClose

1266

1267 packetID = self.sendSMB(packet)

1268 ans = self.recvSMB(packetID)

1269

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

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

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

1273

1274 # ToDo Remove stuff from GlobalFileTable

1275 return True

1276

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

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

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

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

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

1282

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

1284 raise SessionError(STATUS_INVALID_PARAMETER)

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

1286 raise SessionError(STATUS_INVALID_PARAMETER)

1287

1288 packet = self.SMB_PACKET()

1289 packet['Command'] = SMB2_READ

1290 packet['TreeID'] = treeId

1291

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

1293 maxBytesToRead = self._Connection['MaxReadSize']

1294 else:

1295 maxBytesToRead = bytesToRead

1296

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

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

1299 else:

1300 maxBytesToRead = min(65536,bytesToRead)

1301

1302 smbRead = SMB2Read()

1303 smbRead['Padding'] = 0x50

1304 smbRead['FileID'] = fileId

1305 smbRead['Length'] = maxBytesToRead

1306 smbRead['Offset'] = offset

1307 packet['Data'] = smbRead

1308

1309 packetID = self.sendSMB(packet)

1310 ans = self.recvSMB(packetID)

1311

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

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

1314 retData = readResponse['Buffer']

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

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

1317 return retData

1318

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

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

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

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

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

1324

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

1326 raise SessionError(STATUS_INVALID_PARAMETER)

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

1328 raise SessionError(STATUS_INVALID_PARAMETER)

1329

1330 packet = self.SMB_PACKET()

1331 packet['Command'] = SMB2_WRITE

1332 packet['TreeID'] = treeId

1333

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

1335 maxBytesToWrite = self._Connection['MaxWriteSize']

1336 else:

1337 maxBytesToWrite = bytesToWrite

1338

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

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

1341 else:

1342 maxBytesToWrite = min(65536,bytesToWrite)

1343

1344 smbWrite = SMB2Write()

1345 smbWrite['FileID'] = fileId

1346 smbWrite['Length'] = maxBytesToWrite

1347 smbWrite['Offset'] = offset

1348 smbWrite['WriteChannelInfoOffset'] = 0

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

1350 packet['Data'] = smbWrite

1351

1352 packetID = self.sendSMB(packet)

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

1354 ans = self.recvSMB(packetID)

1355 else:

1356 return maxBytesToWrite

1357

1358 if ans.isValidAnswer(STATUS_SUCCESS):

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

1360 bytesWritten = writeResponse['Count']

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

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

1363 return bytesWritten

1364

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

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

1367 raise SessionError(STATUS_INVALID_PARAMETER)

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

1369 raise SessionError(STATUS_INVALID_PARAMETER)

1370

1371 packet = self.SMB_PACKET()

1372 packet['Command'] = SMB2_QUERY_DIRECTORY

1373 packet['TreeID'] = treeId

1374

1375 queryDirectory = SMB2QueryDirectory()

1376 queryDirectory['FileInformationClass'] = informationClass

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

1378 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED

1379 queryDirectory['FileIndex'] = resumeIndex

1380 queryDirectory['FileID'] = fileId

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

1382 maxBufferSize = self._Connection['MaxReadSize']

1383 queryDirectory['OutputBufferLength'] = maxBufferSize

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

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

1386

1387 packet['Data'] = queryDirectory

1388

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

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

1391

1392 packetID = self.sendSMB(packet)

1393 ans = self.recvSMB(packetID)

1394 if ans.isValidAnswer(STATUS_SUCCESS):

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

1396 return queryDirectoryResponse['Buffer']

1397

1398 def echo(self):

1399 packet = self.SMB_PACKET()

1400 packet['Command'] = SMB2_ECHO

1401 smbEcho = SMB2Echo()

1402 packet['Data'] = smbEcho

1403 packetID = self.sendSMB(packet)

1404 ans = self.recvSMB(packetID)

1405 if ans.isValidAnswer(STATUS_SUCCESS):

1406 return True

1407

1408 def cancel(self, packetID):

1409 packet = self.SMB_PACKET()

1410 packet['Command'] = SMB2_CANCEL

1411 packet['MessageID'] = packetID

1412

1413 smbCancel = SMB2Cancel()

1414

1415 packet['Data'] = smbCancel

1416 self.sendSMB(packet)

1417

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

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

1420 raise SessionError(STATUS_INVALID_PARAMETER)

1421 if fileId is None:

1422 fileId = '\xff'*16

1423 else:

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

1425 raise SessionError(STATUS_INVALID_PARAMETER)

1426

1427 packet = self.SMB_PACKET()

1428 packet['Command'] = SMB2_IOCTL

1429 packet['TreeID'] = treeId

1430

1431 smbIoctl = SMB2Ioctl()

1432 smbIoctl['FileID'] = fileId

1433 smbIoctl['CtlCode'] = ctlCode

1434 smbIoctl['MaxInputResponse'] = maxInputResponse

1435 smbIoctl['MaxOutputResponse'] = maxOutputResponse

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

1437 if len(inputBlob) == 0:

1438 smbIoctl['InputOffset'] = 0

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

1440 else:

1441 smbIoctl['Buffer'] = inputBlob

1442 smbIoctl['OutputOffset'] = 0

1443 smbIoctl['MaxOutputResponse'] = maxOutputResponse

1444 smbIoctl['Flags'] = flags

1445

1446 packet['Data'] = smbIoctl

1447

1448 packetID = self.sendSMB(packet)

1449

1450 if waitAnswer == 0:

1451 return True

1452

1453 ans = self.recvSMB(packetID)

1454

1455 if ans.isValidAnswer(STATUS_SUCCESS):

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

1457 return smbIoctlResponse['Buffer']

1458

1459 def flush(self,treeId, fileId):

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

1461 raise SessionError(STATUS_INVALID_PARAMETER)

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

1463 raise SessionError(STATUS_INVALID_PARAMETER)

1464

1465 packet = self.SMB_PACKET()

1466 packet['Command'] = SMB2_FLUSH

1467 packet['TreeID'] = treeId

1468

1469 smbFlush = SMB2Flush()

1470 smbFlush['FileID'] = fileId

1471

1472 packet['Data'] = smbFlush

1473

1474 packetID = self.sendSMB(packet)

1475 ans = self.recvSMB(packetID)

1476

1477 if ans.isValidAnswer(STATUS_SUCCESS):

1478 return True

1479

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

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

1482 raise SessionError(STATUS_INVALID_PARAMETER)

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

1484 raise SessionError(STATUS_INVALID_PARAMETER)

1485

1486 packet = self.SMB_PACKET()

1487 packet['Command'] = SMB2_LOCK

1488 packet['TreeID'] = treeId

1489

1490 smbLock = SMB2Lock()

1491 smbLock['FileID'] = fileId

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

1493 smbLock['LockSequence'] = lockSequence

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

1495

1496 packet['Data'] = smbLock

1497

1498 packetID = self.sendSMB(packet)

1499 ans = self.recvSMB(packetID)

1500

1501 if ans.isValidAnswer(STATUS_SUCCESS):

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

1503 return True

1504

1505 # ToDo:

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

1507 # do the following:

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

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

1510 # returned to the application.

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

1512 # let BucketNumber = BucketIndex +1.

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

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

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

1516 # BucketSequence.

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

1518

1519 def logoff(self):

1520 packet = self.SMB_PACKET()

1521 packet['Command'] = SMB2_LOGOFF

1522

1523 smbLogoff = SMB2Logoff()

1524

1525 packet['Data'] = smbLogoff

1526

1527 packetID = self.sendSMB(packet)

1528 ans = self.recvSMB(packetID)

1529

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

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

1532 # within the same connection

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

1534 self._Session['Connection'] = 0

1535 self._Session['SessionID'] = 0

1536 self._Session['SigningRequired'] = False

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

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

1539 self._Session['SigningActivated'] = False

1540 return True

1541

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

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

1544 raise SessionError(STATUS_INVALID_PARAMETER)

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

1546 raise SessionError(STATUS_INVALID_PARAMETER)

1547

1548 packet = self.SMB_PACKET()

1549 packet['Command'] = SMB2_QUERY_INFO

1550 packet['TreeID'] = treeId

1551

1552 queryInfo = SMB2QueryInfo()

1553 queryInfo['FileID'] = fileId

1554 queryInfo['InfoType'] = infoType

1555 queryInfo['FileInfoClass'] = fileInfoClass

1556 queryInfo['OutputBufferLength'] = 65535

1557 queryInfo['AdditionalInformation'] = additionalInformation

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

1559 queryInfo['InputBufferOffset'] = 0

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

1561 else:

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

1563 queryInfo['Buffer'] = inputBlob

1564 queryInfo['Flags'] = flags

1565

1566 packet['Data'] = queryInfo

1567 packetID = self.sendSMB(packet)

1568 ans = self.recvSMB(packetID)

1569

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

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

1572 return queryResponse['Buffer']

1573

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

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

1576 raise SessionError(STATUS_INVALID_PARAMETER)

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

1578 raise SessionError(STATUS_INVALID_PARAMETER)

1579

1580 packet = self.SMB_PACKET()

1581 packet['Command'] = SMB2_SET_INFO

1582 packet['TreeID'] = treeId

1583

1584 setInfo = SMB2SetInfo()

1585 setInfo['InfoType'] = infoType

1586 setInfo['FileInfoClass'] = fileInfoClass

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

1588 setInfo['AdditionalInformation'] = additionalInformation

1589 setInfo['FileID'] = fileId

1590 setInfo['Buffer'] = inputBlob

1591

1592 packet['Data'] = setInfo

1593 packetID = self.sendSMB(packet)

1594 ans = self.recvSMB(packetID)

1595

1596 if ans.isValidAnswer(STATUS_SUCCESS):

1597 return True

1598

1599 def getSessionKey(self):

1600 if self.getDialect() >= SMB2_DIALECT_30:

1601 return self._Session['ApplicationKey']

1602 else:

1603 return self._Session['SessionKey']

1604

1605 def setSessionKey(self, key):

1606 if self.getDialect() >= SMB2_DIALECT_30:

1607 self._Session['ApplicationKey'] = key

1608 else:

1609 self._Session['SessionKey'] = key

1610

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

1612 # Higher level functions

1613

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

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

1616 oldPath = ntpath.normpath(oldPath)

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

1618 oldPath = oldPath[1:]

1619

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

1621 newPath = ntpath.normpath(newPath)

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

1623 newPath = newPath[1:]

1624

1625 treeId = self.connectTree(shareName)

1626 fileId = None

1627 try:

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

1629 renameReq = FILE_RENAME_INFORMATION_TYPE_2()

1630 renameReq['ReplaceIfExists'] = 1

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

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

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

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

1635 finally:

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

1637 self.close(treeId, fileId)

1638 self.disconnectTree(treeId)

1639

1640 return True

1641

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

1643 finished = False

1644 writeOffset = offset

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

1646 if len(data) == 0:

1647 break

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

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

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

1651 writeOffset += written

1652 return writeOffset - offset

1653

1654 def isSnapshotRequest(self, path):

1655 #TODO: use a regex here?

1656 return '@GMT-' in path

1657

1658 def timestampForSnapshot(self, path):

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

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

1661 from datetime import datetime

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

1663 fTime *= 10000000

1664 fTime += 116444736000000000

1665

1666 token = SMB2_CREATE_TIMEWARP_TOKEN()

1667 token['Timestamp'] = fTime

1668

1669 ctx = SMB2CreateContext()

1670 ctx['Next'] = 0

1671 ctx['NameOffset'] = 16

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

1673 ctx['DataOffset'] = 24

1674 ctx['DataLength'] = 8

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

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

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

1678

1679 # fix-up the path

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

1681 if path == '\\':

1682 path += '*'

1683 return path, ctx

1684

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

1686 createContexts = None

1687

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

1689 createContexts = []

1690 path, ctx = self.timestampForSnapshot(path)

1691 createContexts.append(ctx)

1692

1693 # ToDo: Handle situations where share is password protected

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

1695 path = ntpath.normpath(path)

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

1697 path = path[1:]

1698

1699 treeId = self.connectTree(shareName)

1700

1701 fileId = None

1702 try:

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

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

1705 FILE_SHARE_WRITE | FILE_SHARE_DELETE,

1706 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0,

1707 createContexts=createContexts)

1708 res = ''

1709 files = []

1710 from impacket import smb

1711 while True:

1712 try:

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

1714 informationClass=FILE_FULL_DIRECTORY_INFORMATION)

1715 nextOffset = 1

1716 while nextOffset != 0:

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

1718 fileInfo.fromString(res)

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

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

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

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

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

1724 nextOffset = fileInfo['NextEntryOffset']

1725 res = res[nextOffset:]

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

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

1728 raise

1729 break

1730 except Exception as e:

1731 print(str(e))

1732 raise

1733 finally:

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

1735 self.close(treeId, fileId)

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

1737

1738 return files

1739

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

1741 # ToDo: Handle situations where share is password protected

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

1743 pathName = ntpath.normpath(pathName)

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

1745 pathName = pathName[1:]

1746

1747 treeId = self.connectTree(shareName)

1748

1749 fileId = None

1750 try:

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

1752 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)

1753 finally:

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

1755 self.close(treeId, fileId)

1756 self.disconnectTree(treeId)

1757

1758 return True

1759

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

1761 # ToDo: Handle situations where share is password protected

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

1763 pathName = ntpath.normpath(pathName)

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

1765 pathName = pathName[1:]

1766

1767 treeId = self.connectTree(shareName)

1768

1769 fileId = None

1770 try:

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

1772 shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,

1773 creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT,

1774 creationDisposition=FILE_OPEN, fileAttributes=0)

1775 from impacket import smb

1776 delete_req = smb.SMBSetFileDispositionInfo()

1777 delete_req['DeletePending'] = True

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

1779 finally:

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

1781 self.close(treeId, fileId)

1782 self.disconnectTree(treeId)

1783

1784 return True

1785

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

1787 # ToDo: Handle situations where share is password protected

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

1789 pathName = ntpath.normpath(pathName)

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

1791 pathName = pathName[1:]

1792

1793 treeId = self.connectTree(shareName)

1794

1795 fileId = None

1796 try:

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

1798 finally:

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

1800 self.close(treeId, fileId)

1801 self.disconnectTree(treeId)

1802

1803 return True

1804

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

1806 createContexts = None

1807

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

1809 createContexts = []

1810 path, ctx = self.timestampForSnapshot(path)

1811 createContexts.append(ctx)

1812

1813 # ToDo: Handle situations where share is password protected

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

1815 path = ntpath.normpath(path)

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

1817 path = path[1:]

1818

1819 treeId = self.connectTree(shareName)

1820 fileId = None

1821 from impacket import smb

1822 try:

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

1824 res = self.queryInfo(treeId, fileId)

1825 fileInfo = smb.SMBQueryFileStandardInfo(res)

1826 fileSize = fileInfo['EndOfFile']

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

1828 # Skip reading 0 bytes files.

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

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

1831 callback(data)

1832 else:

1833 written = 0

1834 toBeRead = fileSize-offset

1835 while written < toBeRead:

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

1837 written += len(data)

1838 offset += len(data)

1839 callback(data)

1840 finally:

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

1842 self.close(treeId, fileId)

1843 self.disconnectTree(treeId)

1844

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

1846 # ToDo: Handle situations where share is password protected

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

1848 path = ntpath.normpath(path)

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

1850 path = path[1:]

1851

1852 treeId = self.connectTree(shareName)

1853 fileId = None

1854 try:

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

1856 finished = False

1857 writeOffset = offset

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

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

1860 if len(data) == 0:

1861 break

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

1863 writeOffset += written

1864 finally:

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

1866 self.close(treeId, fileId)

1867 self.disconnectTree(treeId)

1868

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

1870 pipename = ntpath.basename(pipename)

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

1872 raise SessionError(STATUS_INVALID_PARAMETER)

1873 if len(pipename) > 0xffff:

1874 raise SessionError(STATUS_INVALID_PARAMETER)

1875

1876 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()

1877 pipeWait['Timeout'] = timeout*100000

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

1879 pipeWait['TimeoutSpecified'] = 1

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

1881

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

1883

1884 def getIOCapabilities(self):

1885 res = dict()

1886

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

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

1889 return res

1890

1891

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

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

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

1895 # when implementing new client calls.

1896 get_server_name = getServerName

1897 get_client_name = getClientName

1898 get_server_domain = getServerDomain

1899 get_server_dns_domain_name = getServerDNSDomainName

1900 get_server_dns_host_name = getServerDNSHostName

1901 get_remote_name = getRemoteName

1902 set_remote_name = setRemoteName

1903 get_remote_host = getServerIP

1904 get_server_os = getServerOS

1905 get_server_os_major = getServerOSMajor

1906 get_server_os_minor = getServerOSMinor

1907 get_server_os_build = getServerOSBuild

1908 tree_connect_andx = connectTree

1909 tree_connect = connectTree

1910 connect_tree = connectTree

1911 disconnect_tree = disconnectTree

1912 set_timeout = setTimeout

1913 use_timeout = useTimeout

1914 stor_file = storeFile

1915 retr_file = retrieveFile

1916 list_path = listPath

1917

1918 def close_session(self):

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

1920 self._NetBIOSSession.close()

1921 self._NetBIOSSession = None

1922

1923 def doesSupportNTLMv2(self):

1924 # Always true :P

1925 return True

1926

1927 def is_login_required(self):

1928 # Always true :P

1929 return True

1930

1931 def is_signing_required(self):

1932 return self._Connection['RequireSigning']

1933

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

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

1936 fileName = fileName[1:]

1937

1938 if cmd is not None:

1939 from impacket import smb

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

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

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

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

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

1945

1946 else:

1947 return self.create(treeId, fileName,

1948 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |

1949 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,

1950 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )

1951

1952 def get_socket(self):

1953 return self._NetBIOSSession.get_socket()

1954

1955

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

1957 # ToDo: Handle the custom smb_packet situation

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

1959

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

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

1962

1963 def TransactNamedPipeRecv(self):

1964 ans = self.recvSMB()

1965

1966 if ans.isValidAnswer(STATUS_SUCCESS):

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

1968 return smbIoctlResponse['Buffer']

1969

1970

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

1972 # ToDo: Handle the custom smb_packet situation

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

1974 max_size = self._Connection['MaxReadSize']

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

1976

1977 def list_shared(self):

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

1979 raise SessionError(STATUS_NOT_IMPLEMENTED)

1980

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

1982 # ToDo Return all the attributes of the file

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

1984 fileName = fileName[1:]

1985

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

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

1988

1989 def set_session_key(self, signingKey):

1990 self._Session['SessionKey'] = signingKey

1991 self._Session['SigningActivated'] = True

1992 self._Session['SigningRequired'] = True

1993

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

1995 self._strict_hostname_validation = validate

1996 self._validation_allow_absent = accept_empty

1997 self._accepted_hostname = hostname

1998

1999 def perform_hostname_validation(self):

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

2001 if not self._validation_allow_absent:

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

2003 return

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

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

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