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

2#

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

4#

5# This software is provided under a slightly modified version

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

7# for more information.

8#

9# Description:

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

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

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

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

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

15# it certainly helps following the document way easier.

16#

17# Author:

18# Alberto Solino (@agsolino)

19#

20# ToDo:

21# [X] Implement SMB2_CHANGE_NOTIFY

22# [X] Implement SMB2_QUERY_INFO

23# [X] Implement SMB2_SET_INFO

24# [ ] Implement SMB2_OPLOCK_BREAK

25# [X] Implement SMB3 signing

26# [X] Implement SMB3 encryption

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

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

29#

30

31 from __future__ import division

32 from __future__ import print_function

33

34 import socket

35 import ntpath

36 import random

37 import string

38 import struct

39 from six import indexbytes, b

40 from binascii import a2b_hex

41 from contextlib import contextmanager

42 from pyasn1.type.univ import noValue

43 from Cryptodome.Cipher import AES

44

45 from impacket import nmb, ntlm, uuid, crypto

46 from impacket.smb3structs import *

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

48 STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES

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

50 from impacket.krb5.gssapi import KRB5_AP_REQ

51

52

53# For signing

54 import hashlib, hmac, copy

55

56# Our random number generator

57 try:

58 rand = random.SystemRandom()

59 except NotImplementedError:

60 rand = random

61 pass

62

63# Structs to be used

64 TREE_CONNECT = {

65 'ShareName' : '',

66 'TreeConnectId' : 0,

67 'Session' : 0,

68 'IsDfsShare' : False,

69 # If the client implements the SMB 3.0 dialect,

70 # the client MUST also implement the following

71 'IsCAShare' : False,

72 'EncryptData' : False,

73 'IsScaleoutShare' : False,

74 # Outside the protocol

75 'NumberOfUses' : 0,

76}

77

78 FILE = {

79 'OpenTable' : [],

80 'LeaseKey' : '',

81 'LeaseState' : 0,

82 'LeaseEpoch' : 0,

83}

84

85 OPEN = {

86 'FileID' : '',

87 'TreeConnect' : 0,

88 'Connection' : 0, # Not Used

89 'Oplocklevel' : 0,

90 'Durable' : False,

91 'FileName' : '',

92 'ResilientHandle' : False,

93 'LastDisconnectTime' : 0,

94 'ResilientTimeout' : 0,

95 'OperationBuckets' : [],

96 # If the client implements the SMB 3.0 dialect,

97 # the client MUST implement the following

98 'CreateGuid' : '',

99 'IsPersistent' : False,

100 'DesiredAccess' : '',

101 'ShareMode' : 0,

102 'CreateOption' : '',

103 'FileAttributes' : '',

104 'CreateDisposition' : '',

105}

106

107 REQUEST = {

108 'CancelID' : '',

109 'Message' : '',

110 'Timestamp' : 0,

111}

112

113 CHANNEL = {

114 'SigningKey' : '',

115 'Connection' : 0,

116}

117

118

119 class SessionError(Exception):

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

121 Exception.__init__(self)

122 self.error = error

123 self.packet = packet

124

125 def get_error_code( self ):

126 return self.error

127

128 def get_error_packet( self ):

129 return self.packet

130

131 def __str__( self ):

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

133

134

135 class SMB3:

136 class HostnameValidationException(Exception):

137 pass

138

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

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

141

142 # [MS-SMB2] Section 3

143 self.RequireMessageSigning = False #

144 self.ConnectionTable = {}

145 self.GlobalFileTable = {}

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

147 # Only for SMB 3.0

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

149 self.MaxDialect = []

150 self.RequireSecureNegotiate = False

151

152 # Per Transport Connection Data

153 self._Connection = {

154 # Indexed by SessionID

155 #'SessionTable' : {},

156 # Indexed by MessageID

157 'OutstandingRequests' : {},

158 'OutstandingResponses' : {}, #

159 'SequenceWindow' : 0, #

160 'GSSNegotiateToken' : '', #

161 'MaxTransactSize' : 0, #

162 'MaxReadSize' : 0, #

163 'MaxWriteSize' : 0, #

164 'ServerGuid' : '', #

165 'RequireSigning' : False, #

166 'ServerName' : '', #

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

168 # also implement the following

169 'Dialect' : 0, #

170 'SupportsFileLeasing' : False, #

171 'SupportsMultiCredit' : False, #

172 # If the client implements the SMB 3.0 dialect,

173 # it MUST also implement the following

174 'SupportsDirectoryLeasing' : False, #

175 'SupportsMultiChannel' : False, #

176 'SupportsPersistentHandles': False, #

177 'SupportsEncryption' : False, #

178 'ClientCapabilities' : 0,

179 'ServerCapabilities' : 0, #

180 'ClientSecurityMode' : 0, #

181 'ServerSecurityMode' : 0, #

182 # Outside the protocol

183 'ServerIP' : '', #

184 'ClientName' : '', #

185 #GSSoptions (MutualAuth and Delegate)

186 'GSSoptions' : {},

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

188 }

189

190 self._Session = {

191 'SessionID' : 0, #

192 'TreeConnectTable' : {}, #

193 'SessionKey' : b'', #

194 'SigningRequired' : False, #

195 'Connection' : 0, #

196 'UserCredentials' : '', #

197 'OpenTable' : {}, #

198 # If the client implements the SMB 3.0 dialect,

199 # it MUST also implement the following

200 'ChannelList' : [],

201 'ChannelSequence' : 0,

202 #'EncryptData' : False,

203 'EncryptData' : True,

204 'EncryptionKey' : '',

205 'DecryptionKey' : '',

206 'SigningKey' : '',

207 'ApplicationKey' : b'',

208 # Outside the protocol

209 'SessionFlags' : 0, #

210 'ServerName' : '', #

211 'ServerDomain' : '', #

212 'ServerDNSDomainName' : '', #

213 'ServerDNSHostName' : '', #

214 'ServerOS' : '', #

215 'SigningActivated' : False, #

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

217 'CalculatePreAuthHash' : True,

218 }

219

220 self.SMB_PACKET = SMB2Packet

221

222 self._timeout = timeout

223 self._Connection['ServerIP'] = remote_host

224 self._NetBIOSSession = None

225 self._preferredDialect = preferredDialect

226 self._doKerberos = False

227

228 # Strict host validation - off by default

229 self._strict_hostname_validation = False

230 self._validation_allow_absent = True

231 self._accepted_hostname = ''

232

233 self.__userName = ''

234 self.__password = ''

235 self.__domain = ''

236 self.__lmhash = ''

237 self.__nthash = ''

238 self.__kdc = ''

239 self.__aesKey = ''

240 self.__TGT = None

241 self.__TGS = None

242

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

244 self._Connection['ServerName'] = remote_host

245 else:

246 self._Connection['ServerName'] = remote_name

247

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

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

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

251 else:

252 self._Connection['ClientName'] = my_name

253

254 if session is None:

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

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

257 my_name = socket.gethostname()

258 i = my_name.find('.')

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

260 my_name = my_name[:i]

261

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

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

264 else:

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

266

267 self.negotiateSession(preferredDialect)

268 else:

269 self._NetBIOSSession = session

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

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

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

273 self.negotiateSession(preferredDialect, negSessionResponse)

274

275 def printStatus(self):

276 print("CONNECTION")

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

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

279 print()

280 print("SESSION")

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

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

283

284 def __UpdateConnectionPreAuthHash(self, data):

285 from Cryptodome.Hash import SHA512

286 calculatedHash = SHA512.new()

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

288 calculatedHash.update(data)

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

290

291 def __UpdatePreAuthHash(self, data):

292 from Cryptodome.Hash import SHA512

293 calculatedHash = SHA512.new()

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

295 calculatedHash.update(data)

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

297

298 def getKerberos(self):

299 return self._doKerberos

300

301 def getServerName(self):

302 return self._Session['ServerName']

303

304 def getClientName(self):

305 return self._Session['ClientName']

306

307 def getRemoteName(self):

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

309 return self._Connection['ServerName']

310 return self._Session['ServerName']

311

312 def setRemoteName(self, name):

313 self._Session['ServerName'] = name

314 return True

315

316 def getServerIP(self):

317 return self._Connection['ServerIP']

318

319 def getServerDomain(self):

320 return self._Session['ServerDomain']

321

322 def getServerDNSDomainName(self):

323 return self._Session['ServerDNSDomainName']

324

325 def getServerDNSHostName(self):

326 return self._Session['ServerDNSHostName']

327

328 def getServerOS(self):

329 return self._Session['ServerOS']

330

331 def getServerOSMajor(self):

332 return self._Session['ServerOSMajor']

333

334 def getServerOSMinor(self):

335 return self._Session['ServerOSMinor']

336

337 def getServerOSBuild(self):

338 return self._Session['ServerOSBuild']

339

340 def isGuestSession(self):

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

342

343 def setTimeout(self, timeout):

344 self._timeout = timeout

345

346 @contextmanager

347 def useTimeout(self, timeout):

348 prev_timeout = self.getTimeout(timeout)

349 try:

350 yield

351 finally:

352 self.setTimeout(prev_timeout)

353

354 def getDialect(self):

355 return self._Connection['Dialect']

356

357 def signSMB(self, packet):

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

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

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

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

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

363 else:

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

365 p = packet.getData()

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

367 packet['Signature'] = signature

368

369 def sendSMB(self, packet):

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

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

372

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

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

375 # SMB2 header to Session.ChannelSequence

376

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

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

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

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

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

382

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

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

385 packet['CreditCharge'] = 1

386

387 # Standard credit request after negotiating protocol

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

389 packet['CreditRequestResponse'] = 127

390

391 messageId = packet['MessageID']

392

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

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

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

396 packet['Flags'] = SMB2_FLAGS_SIGNED

397 self.signSMB(packet)

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

399 packet['Flags'] = SMB2_FLAGS_SIGNED

400 self.signSMB(packet)

401

402 if packet['Command'] is SMB2_NEGOTIATE:

403 data = packet.getData()

404 self.__UpdateConnectionPreAuthHash(data)

405 self._Session['CalculatePreAuthHash'] = False

406

407 if packet['Command'] is SMB2_SESSION_SETUP:

408 self._Session['CalculatePreAuthHash'] = True

409

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

411 plainText = packet.getData()

412 transformHeader = SMB2_TRANSFORM_HEADER()

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

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

415 transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM

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

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

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

419 cipherText = cipher.encrypt(plainText)

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

421 packet = transformHeader.getData() + cipherText

422

423 self._NetBIOSSession.send_packet(packet)

424 else:

425 data = packet.getData()

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

427 self.__UpdatePreAuthHash(data)

428

429 self._NetBIOSSession.send_packet(data)

430

431 return messageId

432

433 def recvSMB(self, packetID = None):

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

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

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

437

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

439

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

441 # Packet is encrypted

442 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())

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

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

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

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

447 packet = SMB2Packet(plainText)

448 else:

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

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

451 # see [MS-ERREF] section 2.3.

452 packet = SMB2Packet(data.get_trailer())

453

454 # Loop while we receive pending requests

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

456 status = STATUS_PENDING

457 while status == STATUS_PENDING:

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

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

460 packet = SMB2Packet(data.get_trailer())

461 else:

462 # Packet is encrypted

463 transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())

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

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

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

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

468 packet = SMB2Packet(plainText)

469 status = packet['Status']

470

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

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

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

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

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

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

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

478 return packet

479 else:

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

481 return self.recvSMB(packetID)

482

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

484 # Let's store some data for later use

485 self._Connection['ClientSecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

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

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

488 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION

489 currentDialect = SMB2_DIALECT_WILDCARD

490

491 # Do we have a negSessionPacket already?

492 if negSessionResponse is not None:

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

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

495 currentDialect = negResp['DialectRevision']

496

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

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

499

500 packet = self.SMB_PACKET()

501 packet['Command'] = SMB2_NEGOTIATE

502 negSession = SMB2Negotiate()

503

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

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

506 negSession['ClientGuid'] = self.ClientGuid

507 if preferredDialect is not None:

508 negSession['Dialects'] = [preferredDialect]

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

510 # Build the Contexts

511 contextData = SMB311ContextData()

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

513 contextData['NegotiateContextCount'] = 0

514 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_PREAUTH_INTEGRITY_CAPABILITIES

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

516 negotiateContext = SMB2NegotiateContext()

517 negotiateContext['ContextType'] = SMB2_PREAUTH_INTEGRITY_CAPABILITIES

518

519 preAuthIntegrityCapabilities = SMB2PreAuthIntegrityCapabilities()

520 preAuthIntegrityCapabilities['HashAlgorithmCount'] = 1

521 preAuthIntegrityCapabilities['SaltLength'] = 32

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

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

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

525

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

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

528 contextData['NegotiateContextCount'] += 1

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

530

531 # Add an SMB2_NEGOTIATE_CONTEXT with ContextType as SMB2_ENCRYPTION_CAPABILITIES

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

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

534

535 negotiateContext2 = SMB2NegotiateContext ()

536 negotiateContext2['ContextType'] = SMB2_ENCRYPTION_CAPABILITIES

537

538 encryptionCapabilities = SMB2EncryptionCapabilities()

539 encryptionCapabilities['CipherCount'] = 1

540 encryptionCapabilities['Ciphers'] = 1

541

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

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

544 contextData['NegotiateContextCount'] += 1

545

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

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

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

549 # previous negotiate context.

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

551

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

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

554

555 else:

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

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

558 packet['Data'] = negSession

559

560 packetID = self.sendSMB(packet)

561 ans = self.recvSMB(packetID)

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

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

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

565 self.__UpdateConnectionPreAuthHash(ans.rawData)

566

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

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

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

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

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

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

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

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

575 self._Connection['RequireSigning'] = True

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

577 # Always Sign

578 self._Connection['RequireSigning'] = True

579

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

581 self._Connection['SupportsFileLeasing'] = True

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

583 self._Connection['SupportsMultiCredit'] = True

584

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

586 # Switching to the right packet format

587 self.SMB_PACKET = SMB3Packet

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

589 self._Connection['SupportsDirectoryLeasing'] = True

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

591 self._Connection['SupportsMultiChannel'] = True

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

593 self._Connection['SupportsPersistentHandles'] = True

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

595 self._Connection['SupportsEncryption'] = True

596

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

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

599

600 def getCredentials(self):

601 return (

602 self.__userName,

603 self.__password,

604 self.__domain,

605 self.__lmhash,

606 self.__nthash,

607 self.__aesKey,

608 self.__TGT,

609 self.__TGS)

610

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

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

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

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

615 # TGS['sessionKey'] = the sessionKey

616 # If we have hashes, normalize them

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

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

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

620 try: # just in case they were converted already

621 lmhash = a2b_hex(lmhash)

622 nthash = a2b_hex(nthash)

623 except:

624 pass

625

626 self.__userName = user

627 self.__password = password

628 self.__domain = domain

629 self.__lmhash = lmhash

630 self.__nthash = nthash

631 self.__kdc = kdcHost

632 self.__aesKey = aesKey

633 self.__TGT = TGT

634 self.__TGS = TGS

635 self._doKerberos= True

636

637 sessionSetup = SMB2SessionSetup()

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

639 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED

640 else:

641 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

642

643 sessionSetup['Flags'] = 0

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

645

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

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

648 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS

649 from impacket.krb5 import constants

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

651 from pyasn1.codec.der import decoder, encoder

652 import datetime

653

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

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

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

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

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

659 else:

660 tgt = TGT['KDC_REP']

661 cipher = TGT['cipher']

662 sessionKey = TGT['sessionKey']

663

664 # Save the ticket

665 # If you want, for debugging purposes

666# from impacket.krb5.ccache import CCache

667# ccache = CCache()

668# try:

669# if TGS is None:

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

671# else:

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

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

674# except Exception, e:

675# print e

676# pass

677

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

679

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

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

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

683 else:

684 tgs = TGS['KDC_REP']

685 cipher = TGS['cipher']

686 sessionKey = TGS['sessionKey']

687

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

689

690 blob = SPNEGO_NegTokenInit()

691

692 # Kerberos

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

694

695 # Let's extract the ticket from the TGS

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

697 ticket = Ticket()

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

699

700 # Now let's build the AP_REQ

701 apReq = AP_REQ()

702 apReq['pvno'] = 5

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

704

705 #Handle mutual authentication

706 opts = list()

707

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

709 from impacket.krb5.constants import APOptions

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

711

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

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

714

715 authenticator = Authenticator()

716 authenticator['authenticator-vno'] = 5

717 authenticator['crealm'] = domain

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

719 now = datetime.datetime.utcnow()

720

721 authenticator['cusec'] = now.microsecond

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

723

724 encodedAuthenticator = encoder.encode(authenticator)

725

726 # Key Usage 11

727 # AP-REQ Authenticator (includes application authenticator

728 # subkey), encrypted with the application session key

729 # (Section 5.5.1)

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

731

732 apReq['authenticator'] = noValue

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

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

735

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

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

738

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

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

741

742 packet = self.SMB_PACKET()

743 packet['Command'] = SMB2_SESSION_SETUP

744 packet['Data'] = sessionSetup

745

746 #Initiate session preauth hash

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

748

749 packetID = self.sendSMB(packet)

750 ans = self.recvSMB(packetID)

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

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

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

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

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

756

757

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

759 #Lets get the session key in the AP_REP

760 from impacket.krb5.asn1 import AP_REP, EncAPRepPart

761 from impacket.krb5.crypto import Key, _enctype_table

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

763

764 #in [KILE] 3.1.1.2:

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

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

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

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

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

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

771 # defined in [RFC4120] section 5.5.1).

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

773

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

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

776

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

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

779

780 # Key Usage 12

781 # AP-REP encrypted part (includes application session

782 # subkey), encrypted with the application session key

783 # (Section 5.5.2)

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

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

786

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

788

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

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

791

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

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

794

795 else:

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

797

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

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

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

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

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

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

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

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

806 else:

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

808 b"SmbSign\x00", 128)

809

810 # Do not encrypt anonymous connections

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

812 self._Connection['SupportsEncryption'] = False

813

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

815 self._Session['SigningActivated'] = True

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

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

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

819 # Application Key

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

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

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

823 # Encryption Key

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

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

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

827 # (note the blank space at the end)

828 # Decryption Key

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

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

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

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

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

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

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

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

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

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

839 else:

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

841 b"SmbRpc\x00", 128)

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

843 b"ServerIn \x00", 128)

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

845 b"ServerOut\x00", 128)

846

847 self._Session['CalculatePreAuthHash'] = False

848 return True

849 else:

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

851 # within the same connection

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

853 self._Session['Connection'] = 0

854 self._Session['SessionID'] = 0

855 self._Session['SigningRequired'] = False

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

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

858 self._Session['SigningActivated'] = False

859 self._Session['CalculatePreAuthHash'] = False

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

861 raise Exception('Unsuccessful Login')

862

863

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

865 # If we have hashes, normalize them

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

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

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

869 try: # just in case they were converted already

870 lmhash = a2b_hex(lmhash)

871 nthash = a2b_hex(nthash)

872 except:

873 pass

874

875 self.__userName = user

876 self.__password = password

877 self.__domain = domain

878 self.__lmhash = lmhash

879 self.__nthash = nthash

880 self.__aesKey = ''

881 self.__TGT = None

882 self.__TGS = None

883

884 sessionSetup = SMB2SessionSetup()

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

886 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED

887 else:

888 sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED

889

890 sessionSetup['Flags'] = 0

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

892

893 # Let's build a NegTokenInit with the NTLMSSP

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

895

896 blob = SPNEGO_NegTokenInit()

897

898 # NTLMSSP

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

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

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

902

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

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

905

906 # ToDo:

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

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

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

910 # channel being established.

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

912 # The PreviousSessionId field MUST be set to zero.

913

914 packet = self.SMB_PACKET()

915 packet['Command'] = SMB2_SESSION_SETUP

916 packet['Data'] = sessionSetup

917

918 packetID = self.sendSMB(packet)

919 ans = self.recvSMB(packetID)

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

921 self.__UpdatePreAuthHash (ans.rawData)

922

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

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

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

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

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

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

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

930

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

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

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

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

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

936 try:

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

938 except:

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

940 pass

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

942 try:

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

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

945 except:

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

947 pass

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

949 try:

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

951 except:

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

953 pass

954

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

956 try:

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

958 except:

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

960 pass

961

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

963 self.perform_hostname_validation()

964

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

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

967 version = ntlmChallenge['Version']

968

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

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

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

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

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

974

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

976

977

978

979 respToken2 = SPNEGO_NegTokenResp()

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

981

982 # Reusing the previous structure

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

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

985

986 packetID = self.sendSMB(packet)

987 packet = self.recvSMB(packetID)

988

989 # Let's calculate Key Materials before moving on

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

991 self._Session['SessionKey'] = exportedSessionKey

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

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

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

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

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

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

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

999 b"SMBSigningKey\x00",

1000 self._Session['PreauthIntegrityHashValue'],

1001 128)

1002 else:

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

1004 b"SmbSign\x00", 128)

1005 try:

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

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

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

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

1010

1011 # Do not encrypt anonymous connections

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

1013 self._Connection['SupportsEncryption'] = False

1014

1015 # Calculate the key derivations for dialect 3.0

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

1017 self._Session['SigningActivated'] = True

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

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

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

1021 # Application Key

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

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

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

1025 # Encryption Key

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

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

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

1029 # (note the blank space at the end)

1030 # Decryption Key

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

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

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

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

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

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

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

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

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

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

1041

1042 else:

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

1044 b"SmbRpc\x00", 128)

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

1046 b"ServerIn \x00", 128)

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

1048 b"ServerOut\x00", 128)

1049 self._Session['CalculatePreAuthHash'] = False

1050 return True

1051 except:

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

1053 # within the same connection

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

1055 self._Session['Connection'] = 0

1056 self._Session['SessionID'] = 0

1057 self._Session['SigningRequired'] = False

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

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

1060 self._Session['SigningActivated'] = False

1061 self._Session['CalculatePreAuthHash'] = False

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

1063 raise

1064

1065 def connectTree(self, share):

1066

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

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

1069

1070 #print self._Session['TreeConnectTable']

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

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

1073 # Already connected, no need to reconnect

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

1075 treeEntry['NumberOfUses'] += 1

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

1077 return treeEntry['TreeConnectId']

1078

1079 #path = share

1080 try:

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

1082 remoteHost = sockaddr[0]

1083 except:

1084 remoteHost = self._Connection['ServerIP']

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

1086

1087 treeConnect = SMB2TreeConnect()

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

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

1090

1091 packet = self.SMB_PACKET()

1092 packet['Command'] = SMB2_TREE_CONNECT

1093 packet['Data'] = treeConnect

1094 packetID = self.sendSMB(packet)

1095 packet = self.recvSMB(packetID)

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

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

1098 treeEntry = copy.deepcopy(TREE_CONNECT)

1099 treeEntry['ShareName'] = share

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

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

1102 treeEntry['NumberOfUses'] += 1

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

1104 treeEntry['IsDfsShare'] = True

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

1106 treeEntry['IsCAShare'] = True

1107

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

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

1110 treeEntry['EncryptData'] = True

1111 # ToDo: This and what follows

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

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

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

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

1116 treeEntry['IsScaleoutShare'] = True

1117

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

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

1120

1121 return packet['TreeID']

1122

1123 def disconnectTree(self, treeId):

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

1125 raise SessionError(STATUS_INVALID_PARAMETER)

1126

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

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

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

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

1131 treeEntry['NumberOfUses'] -= 1

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

1133 return True

1134

1135 packet = self.SMB_PACKET()

1136 packet['Command'] = SMB2_TREE_DISCONNECT

1137 packet['TreeID'] = treeId

1138 treeDisconnect = SMB2TreeDisconnect()

1139 packet['Data'] = treeDisconnect

1140 packetID = self.sendSMB(packet)

1141 packet = self.recvSMB(packetID)

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

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

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

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

1146 filesIDToBeRemoved = []

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

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

1149 filesIDToBeRemoved.append(fileID)

1150 for fileIDToBeRemoved in filesIDToBeRemoved:

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

1152 return True

1153

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

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

1156 raise SessionError(STATUS_INVALID_PARAMETER)

1157

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

1159 if len(fileName) > 0:

1160 fileName = ntpath.normpath(fileName)

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

1162 fileName = fileName[1:]

1163

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

1165 pathName = fileName

1166 else:

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

1168

1169 fileEntry = copy.deepcopy(FILE)

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

1171 fileEntry['LeaseState'] = SMB2_LEASE_NONE

1172 self.GlobalFileTable[pathName] = fileEntry

1173

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

1175 # Is this file NOT on the root directory?

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

1177 parentDir = ntpath.dirname(pathName)

1178 if parentDir in self.GlobalFileTable:

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

1180 else:

1181 parentEntry = copy.deepcopy(FILE)

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

1183 parentEntry['LeaseState'] = SMB2_LEASE_NONE

1184 self.GlobalFileTable[parentDir] = parentEntry

1185

1186 packet = self.SMB_PACKET()

1187 packet['Command'] = SMB2_CREATE

1188 packet['TreeID'] = treeId

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

1190 packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS

1191

1192 smb2Create = SMB2Create()

1193 smb2Create['SecurityFlags'] = 0

1194 smb2Create['RequestedOplockLevel'] = oplockLevel

1195 smb2Create['ImpersonationLevel'] = impersonationLevel

1196 smb2Create['DesiredAccess'] = desiredAccess

1197 smb2Create['FileAttributes'] = fileAttributes

1198 smb2Create['ShareAccess'] = shareMode

1199 smb2Create['CreateDisposition'] = creationDisposition

1200 smb2Create['CreateOptions'] = creationOptions

1201

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

1203 if fileName != '':

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

1205 else:

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

1207

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

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

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

1211

1212 # pad offset to 8-byte align

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

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

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

1216

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

1218 smb2Create['Buffer'] += contextsBuf

1219 else:

1220 smb2Create['CreateContextsOffset'] = 0

1221 smb2Create['CreateContextsLength'] = 0

1222

1223 packet['Data'] = smb2Create

1224

1225 packetID = self.sendSMB(packet)

1226 ans = self.recvSMB(packetID)

1227 if ans.isValidAnswer(STATUS_SUCCESS):

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

1229

1230 openFile = copy.deepcopy(OPEN)

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

1232 openFile['TreeConnect'] = treeId

1233 openFile['Oplocklevel'] = oplockLevel

1234 openFile['Durable'] = False

1235 openFile['ResilientHandle'] = False

1236 openFile['LastDisconnectTime'] = 0

1237 openFile['FileName'] = pathName

1238

1239 # ToDo: Complete the OperationBuckets

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

1241 openFile['DesiredAccess'] = oplockLevel

1242 openFile['ShareMode'] = oplockLevel

1243 openFile['CreateOptions'] = oplockLevel

1244 openFile['FileAttributes'] = oplockLevel

1245 openFile['CreateDisposition'] = oplockLevel

1246

1247 # ToDo: Process the contexts

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

1249

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

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

1252 # In our case, str(FileID)

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

1254

1255 def close(self, treeId, fileId):

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

1257 raise SessionError(STATUS_INVALID_PARAMETER)

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

1259 raise SessionError(STATUS_INVALID_PARAMETER)

1260

1261 packet = self.SMB_PACKET()

1262 packet['Command'] = SMB2_CLOSE

1263 packet['TreeID'] = treeId

1264

1265 smbClose = SMB2Close()

1266 smbClose['Flags'] = 0

1267 smbClose['FileID'] = fileId

1268

1269 packet['Data'] = smbClose

1270

1271 packetID = self.sendSMB(packet)

1272 ans = self.recvSMB(packetID)

1273

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

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

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

1277

1278 # ToDo Remove stuff from GlobalFileTable

1279 return True

1280

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

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

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

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

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

1286

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

1288 raise SessionError(STATUS_INVALID_PARAMETER)

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

1290 raise SessionError(STATUS_INVALID_PARAMETER)

1291

1292 packet = self.SMB_PACKET()

1293 packet['Command'] = SMB2_READ

1294 packet['TreeID'] = treeId

1295

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

1297 maxBytesToRead = self._Connection['MaxReadSize']

1298 else:

1299 maxBytesToRead = bytesToRead

1300

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

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

1303 else:

1304 maxBytesToRead = min(65536,bytesToRead)

1305

1306 smbRead = SMB2Read()

1307 smbRead['Padding'] = 0x50

1308 smbRead['FileID'] = fileId

1309 smbRead['Length'] = maxBytesToRead

1310 smbRead['Offset'] = offset

1311 packet['Data'] = smbRead

1312

1313 packetID = self.sendSMB(packet)

1314 ans = self.recvSMB(packetID)

1315

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

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

1318 retData = readResponse['Buffer']

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

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

1321 return retData

1322

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

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

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

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

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

1328

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

1330 raise SessionError(STATUS_INVALID_PARAMETER)

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

1332 raise SessionError(STATUS_INVALID_PARAMETER)

1333

1334 packet = self.SMB_PACKET()

1335 packet['Command'] = SMB2_WRITE

1336 packet['TreeID'] = treeId

1337

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

1339 maxBytesToWrite = self._Connection['MaxWriteSize']

1340 else:

1341 maxBytesToWrite = bytesToWrite

1342

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

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

1345 else:

1346 maxBytesToWrite = min(65536,bytesToWrite)

1347

1348 smbWrite = SMB2Write()

1349 smbWrite['FileID'] = fileId

1350 smbWrite['Length'] = maxBytesToWrite

1351 smbWrite['Offset'] = offset

1352 smbWrite['WriteChannelInfoOffset'] = 0

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

1354 packet['Data'] = smbWrite

1355

1356 packetID = self.sendSMB(packet)

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

1358 ans = self.recvSMB(packetID)

1359 else:

1360 return maxBytesToWrite

1361

1362 if ans.isValidAnswer(STATUS_SUCCESS):

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

1364 bytesWritten = writeResponse['Count']

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

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

1367 return bytesWritten

1368

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

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

1371 raise SessionError(STATUS_INVALID_PARAMETER)

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

1373 raise SessionError(STATUS_INVALID_PARAMETER)

1374

1375 packet = self.SMB_PACKET()

1376 packet['Command'] = SMB2_QUERY_DIRECTORY

1377 packet['TreeID'] = treeId

1378

1379 queryDirectory = SMB2QueryDirectory()

1380 queryDirectory['FileInformationClass'] = informationClass

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

1382 queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED

1383 queryDirectory['FileIndex'] = resumeIndex

1384 queryDirectory['FileID'] = fileId

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

1386 maxBufferSize = self._Connection['MaxReadSize']

1387 queryDirectory['OutputBufferLength'] = maxBufferSize

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

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

1390

1391 packet['Data'] = queryDirectory

1392

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

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

1395

1396 packetID = self.sendSMB(packet)

1397 ans = self.recvSMB(packetID)

1398 if ans.isValidAnswer(STATUS_SUCCESS):

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

1400 return queryDirectoryResponse['Buffer']

1401

1402 def echo(self):

1403 packet = self.SMB_PACKET()

1404 packet['Command'] = SMB2_ECHO

1405 smbEcho = SMB2Echo()

1406 packet['Data'] = smbEcho

1407 packetID = self.sendSMB(packet)

1408 ans = self.recvSMB(packetID)

1409 if ans.isValidAnswer(STATUS_SUCCESS):

1410 return True

1411

1412 def cancel(self, packetID):

1413 packet = self.SMB_PACKET()

1414 packet['Command'] = SMB2_CANCEL

1415 packet['MessageID'] = packetID

1416

1417 smbCancel = SMB2Cancel()

1418

1419 packet['Data'] = smbCancel

1420 self.sendSMB(packet)

1421

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

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

1424 raise SessionError(STATUS_INVALID_PARAMETER)

1425 if fileId is None:

1426 fileId = '\xff'*16

1427 else:

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

1429 raise SessionError(STATUS_INVALID_PARAMETER)

1430

1431 packet = self.SMB_PACKET()

1432 packet['Command'] = SMB2_IOCTL

1433 packet['TreeID'] = treeId

1434

1435 smbIoctl = SMB2Ioctl()

1436 smbIoctl['FileID'] = fileId

1437 smbIoctl['CtlCode'] = ctlCode

1438 smbIoctl['MaxInputResponse'] = maxInputResponse

1439 smbIoctl['MaxOutputResponse'] = maxOutputResponse

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

1441 if len(inputBlob) == 0:

1442 smbIoctl['InputOffset'] = 0

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

1444 else:

1445 smbIoctl['Buffer'] = inputBlob

1446 smbIoctl['OutputOffset'] = 0

1447 smbIoctl['MaxOutputResponse'] = maxOutputResponse

1448 smbIoctl['Flags'] = flags

1449

1450 packet['Data'] = smbIoctl

1451

1452 packetID = self.sendSMB(packet)

1453

1454 if waitAnswer == 0:

1455 return True

1456

1457 ans = self.recvSMB(packetID)

1458

1459 if ans.isValidAnswer(STATUS_SUCCESS):

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

1461 return smbIoctlResponse['Buffer']

1462

1463 def flush(self,treeId, fileId):

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

1465 raise SessionError(STATUS_INVALID_PARAMETER)

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

1467 raise SessionError(STATUS_INVALID_PARAMETER)

1468

1469 packet = self.SMB_PACKET()

1470 packet['Command'] = SMB2_FLUSH

1471 packet['TreeID'] = treeId

1472

1473 smbFlush = SMB2Flush()

1474 smbFlush['FileID'] = fileId

1475

1476 packet['Data'] = smbFlush

1477

1478 packetID = self.sendSMB(packet)

1479 ans = self.recvSMB(packetID)

1480

1481 if ans.isValidAnswer(STATUS_SUCCESS):

1482 return True

1483

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

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

1486 raise SessionError(STATUS_INVALID_PARAMETER)

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

1488 raise SessionError(STATUS_INVALID_PARAMETER)

1489

1490 packet = self.SMB_PACKET()

1491 packet['Command'] = SMB2_LOCK

1492 packet['TreeID'] = treeId

1493

1494 smbLock = SMB2Lock()

1495 smbLock['FileID'] = fileId

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

1497 smbLock['LockSequence'] = lockSequence

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

1499

1500 packet['Data'] = smbLock

1501

1502 packetID = self.sendSMB(packet)

1503 ans = self.recvSMB(packetID)

1504

1505 if ans.isValidAnswer(STATUS_SUCCESS):

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

1507 return True

1508

1509 # ToDo:

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

1511 # do the following:

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

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

1514 # returned to the application.

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

1516 # let BucketNumber = BucketIndex +1.

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

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

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

1520 # BucketSequence.

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

1522

1523 def logoff(self):

1524 packet = self.SMB_PACKET()

1525 packet['Command'] = SMB2_LOGOFF

1526

1527 smbLogoff = SMB2Logoff()

1528

1529 packet['Data'] = smbLogoff

1530

1531 packetID = self.sendSMB(packet)

1532 ans = self.recvSMB(packetID)

1533

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

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

1536 # within the same connection

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

1538 self._Session['Connection'] = 0

1539 self._Session['SessionID'] = 0

1540 self._Session['SigningRequired'] = False

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

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

1543 self._Session['SigningActivated'] = False

1544 return True

1545

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

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

1548 raise SessionError(STATUS_INVALID_PARAMETER)

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

1550 raise SessionError(STATUS_INVALID_PARAMETER)

1551

1552 packet = self.SMB_PACKET()

1553 packet['Command'] = SMB2_QUERY_INFO

1554 packet['TreeID'] = treeId

1555

1556 queryInfo = SMB2QueryInfo()

1557 queryInfo['FileID'] = fileId

1558 queryInfo['InfoType'] = infoType

1559 queryInfo['FileInfoClass'] = fileInfoClass

1560 queryInfo['OutputBufferLength'] = 65535

1561 queryInfo['AdditionalInformation'] = additionalInformation

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

1563 queryInfo['InputBufferOffset'] = 0

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

1565 else:

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

1567 queryInfo['Buffer'] = inputBlob

1568 queryInfo['Flags'] = flags

1569

1570 packet['Data'] = queryInfo

1571 packetID = self.sendSMB(packet)

1572 ans = self.recvSMB(packetID)

1573

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

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

1576 return queryResponse['Buffer']

1577

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

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

1580 raise SessionError(STATUS_INVALID_PARAMETER)

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

1582 raise SessionError(STATUS_INVALID_PARAMETER)

1583

1584 packet = self.SMB_PACKET()

1585 packet['Command'] = SMB2_SET_INFO

1586 packet['TreeID'] = treeId

1587

1588 setInfo = SMB2SetInfo()

1589 setInfo['InfoType'] = infoType

1590 setInfo['FileInfoClass'] = fileInfoClass

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

1592 setInfo['AdditionalInformation'] = additionalInformation

1593 setInfo['FileID'] = fileId

1594 setInfo['Buffer'] = inputBlob

1595

1596 packet['Data'] = setInfo

1597 packetID = self.sendSMB(packet)

1598 ans = self.recvSMB(packetID)

1599

1600 if ans.isValidAnswer(STATUS_SUCCESS):

1601 return True

1602

1603 def getSessionKey(self):

1604 if self.getDialect() >= SMB2_DIALECT_30:

1605 return self._Session['ApplicationKey']

1606 else:

1607 return self._Session['SessionKey']

1608

1609 def setSessionKey(self, key):

1610 if self.getDialect() >= SMB2_DIALECT_30:

1611 self._Session['ApplicationKey'] = key

1612 else:

1613 self._Session['SessionKey'] = key

1614

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

1616 # Higher level functions

1617

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

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

1620 oldPath = ntpath.normpath(oldPath)

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

1622 oldPath = oldPath[1:]

1623

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

1625 newPath = ntpath.normpath(newPath)

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

1627 newPath = newPath[1:]

1628

1629 treeId = self.connectTree(shareName)

1630 fileId = None

1631 try:

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

1633 renameReq = FILE_RENAME_INFORMATION_TYPE_2()

1634 renameReq['ReplaceIfExists'] = 1

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

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

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

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

1639 finally:

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

1641 self.close(treeId, fileId)

1642 self.disconnectTree(treeId)

1643

1644 return True

1645

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

1647 finished = False

1648 writeOffset = offset

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

1650 if len(data) == 0:

1651 break

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

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

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

1655 writeOffset += written

1656 return writeOffset - offset

1657

1658 def isSnapshotRequest(self, path):

1659 #TODO: use a regex here?

1660 return '@GMT-' in path

1661

1662 def timestampForSnapshot(self, path):

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

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

1665 from datetime import datetime

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

1667 fTime *= 10000000

1668 fTime += 116444736000000000

1669

1670 token = SMB2_CREATE_TIMEWARP_TOKEN()

1671 token['Timestamp'] = fTime

1672

1673 ctx = SMB2CreateContext()

1674 ctx['Next'] = 0

1675 ctx['NameOffset'] = 16

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

1677 ctx['DataOffset'] = 24

1678 ctx['DataLength'] = 8

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

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

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

1682

1683 # fix-up the path

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

1685 if path == '\\':

1686 path += '*'

1687 return path, ctx

1688

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

1690 createContexts = None

1691

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

1693 createContexts = []

1694 path, ctx = self.timestampForSnapshot(path)

1695 createContexts.append(ctx)

1696

1697 # ToDo: Handle situations where share is password protected

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

1699 path = ntpath.normpath(path)

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

1701 path = path[1:]

1702

1703 treeId = self.connectTree(shareName)

1704

1705 fileId = None

1706 try:

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

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

1709 FILE_SHARE_WRITE | FILE_SHARE_DELETE,

1710 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0,

1711 createContexts=createContexts)

1712 res = ''

1713 files = []

1714 from impacket import smb

1715 while True:

1716 try:

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

1718 informationClass=FILE_FULL_DIRECTORY_INFORMATION)

1719 nextOffset = 1

1720 while nextOffset != 0:

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

1722 fileInfo.fromString(res)

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

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

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

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

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

1728 nextOffset = fileInfo['NextEntryOffset']

1729 res = res[nextOffset:]

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

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

1732 raise

1733 break

1734 except Exception as e:

1735 print(str(e))

1736 raise

1737 finally:

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

1739 self.close(treeId, fileId)

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

1741

1742 return files

1743

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

1745 # ToDo: Handle situations where share is password protected

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

1747 pathName = ntpath.normpath(pathName)

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

1749 pathName = pathName[1:]

1750

1751 treeId = self.connectTree(shareName)

1752

1753 fileId = None

1754 try:

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

1756 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)

1757 finally:

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

1759 self.close(treeId, fileId)

1760 self.disconnectTree(treeId)

1761

1762 return True

1763

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

1765 # ToDo: Handle situations where share is password protected

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

1767 pathName = ntpath.normpath(pathName)

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

1769 pathName = pathName[1:]

1770

1771 treeId = self.connectTree(shareName)

1772

1773 fileId = None

1774 try:

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

1776 shareMode=FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,

1777 creationOptions=FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT,

1778 creationDisposition=FILE_OPEN, fileAttributes=0)

1779 from impacket import smb

1780 delete_req = smb.SMBSetFileDispositionInfo()

1781 delete_req['DeletePending'] = True

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

1783 finally:

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

1785 self.close(treeId, fileId)

1786 self.disconnectTree(treeId)

1787

1788 return True

1789

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

1791 # ToDo: Handle situations where share is password protected

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

1793 pathName = ntpath.normpath(pathName)

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

1795 pathName = pathName[1:]

1796

1797 treeId = self.connectTree(shareName)

1798

1799 fileId = None

1800 try:

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

1802 finally:

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

1804 self.close(treeId, fileId)

1805 self.disconnectTree(treeId)

1806

1807 return True

1808

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

1810 createContexts = None

1811

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

1813 createContexts = []

1814 path, ctx = self.timestampForSnapshot(path)

1815 createContexts.append(ctx)

1816

1817 # ToDo: Handle situations where share is password protected

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

1819 path = ntpath.normpath(path)

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

1821 path = path[1:]

1822

1823 treeId = self.connectTree(shareName)

1824 fileId = None

1825 from impacket import smb

1826 try:

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

1828 res = self.queryInfo(treeId, fileId)

1829 fileInfo = smb.SMBQueryFileStandardInfo(res)

1830 fileSize = fileInfo['EndOfFile']

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

1832 # Skip reading 0 bytes files.

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

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

1835 callback(data)

1836 else:

1837 written = 0

1838 toBeRead = fileSize-offset

1839 while written < toBeRead:

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

1841 written += len(data)

1842 offset += len(data)

1843 callback(data)

1844 finally:

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

1846 self.close(treeId, fileId)

1847 self.disconnectTree(treeId)

1848

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

1850 # ToDo: Handle situations where share is password protected

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

1852 path = ntpath.normpath(path)

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

1854 path = path[1:]

1855

1856 treeId = self.connectTree(shareName)

1857 fileId = None

1858 try:

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

1860 finished = False

1861 writeOffset = offset

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

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

1864 if len(data) == 0:

1865 break

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

1867 writeOffset += written

1868 finally:

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

1870 self.close(treeId, fileId)

1871 self.disconnectTree(treeId)

1872

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

1874 pipename = ntpath.basename(pipename)

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

1876 raise SessionError(STATUS_INVALID_PARAMETER)

1877 if len(pipename) > 0xffff:

1878 raise SessionError(STATUS_INVALID_PARAMETER)

1879

1880 pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()

1881 pipeWait['Timeout'] = timeout*100000

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

1883 pipeWait['TimeoutSpecified'] = 1

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

1885

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

1887

1888 def getIOCapabilities(self):

1889 res = dict()

1890

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

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

1893 return res

1894

1895

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

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

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

1899 # when implementing new client calls.

1900 get_server_name = getServerName

1901 get_client_name = getClientName

1902 get_server_domain = getServerDomain

1903 get_server_dns_domain_name = getServerDNSDomainName

1904 get_server_dns_host_name = getServerDNSHostName

1905 get_remote_name = getRemoteName

1906 set_remote_name = setRemoteName

1907 get_remote_host = getServerIP

1908 get_server_os = getServerOS

1909 get_server_os_major = getServerOSMajor

1910 get_server_os_minor = getServerOSMinor

1911 get_server_os_build = getServerOSBuild

1912 tree_connect_andx = connectTree

1913 tree_connect = connectTree

1914 connect_tree = connectTree

1915 disconnect_tree = disconnectTree

1916 set_timeout = setTimeout

1917 use_timeout = useTimeout

1918 stor_file = storeFile

1919 retr_file = retrieveFile

1920 list_path = listPath

1921

1922 def close_session(self):

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

1924 self._NetBIOSSession.close()

1925 self._NetBIOSSession = None

1926

1927 def doesSupportNTLMv2(self):

1928 # Always true :P

1929 return True

1930

1931 def is_login_required(self):

1932 # Always true :P

1933 return True

1934

1935 def is_signing_required(self):

1936 return self._Connection['RequireSigning']

1937

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

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

1940 fileName = fileName[1:]

1941

1942 if cmd is not None:

1943 from impacket import smb

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

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

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

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

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

1949

1950 else:

1951 return self.create(treeId, fileName,

1952 FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |

1953 FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,

1954 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )

1955

1956 def get_socket(self):

1957 return self._NetBIOSSession.get_socket()

1958

1959

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

1961 # ToDo: Handle the custom smb_packet situation

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

1963

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

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

1966

1967 def TransactNamedPipeRecv(self):

1968 ans = self.recvSMB()

1969

1970 if ans.isValidAnswer(STATUS_SUCCESS):

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

1972 return smbIoctlResponse['Buffer']

1973

1974

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

1976 # ToDo: Handle the custom smb_packet situation

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

1978 max_size = self._Connection['MaxReadSize']

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

1980

1981 def list_shared(self):

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

1983 raise SessionError(STATUS_NOT_IMPLEMENTED)

1984

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

1986 # ToDo Return all the attributes of the file

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

1988 fileName = fileName[1:]

1989

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

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

1992

1993 def set_session_key(self, signingKey):

1994 self._Session['SessionKey'] = signingKey

1995 self._Session['SigningActivated'] = True

1996 self._Session['SigningRequired'] = True

1997

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

1999 self._strict_hostname_validation = validate

2000 self._validation_allow_absent = accept_empty

2001 self._accepted_hostname = hostname

2002

2003 def perform_hostname_validation(self):

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

2005 if not self._validation_allow_absent:

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

2007 return

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

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

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