github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/wmipersist.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  # This script creates/removes a WMI Event Consumer/Filter and link 
     9  # between both to execute Visual Basic based on the WQL filter 
    10  # or timer specified.
    11  #
    12  # Author:
    13  #  beto (@agsolino)
    14  #
    15  # Example: 
    16  #
    17  # write a file toexec.vbs the following:
    18  #	Dim objFS, objFile
    19  #	Set objFS = CreateObject("Scripting.FileSystemObject")
    20  #	Set objFile = objFS.OpenTextFile("C:\ASEC.log", 8, true)
    21  #	objFile.WriteLine "Hey There!"
    22  #	objFile.Close
    23  #
    24  #
    25  # then execute this script this way, VBS will be triggered once
    26  # somebody opens calc.exe:
    27  #
    28  #  wmipersist.py domain.net/adminuser:mypwd@targetHost install -name ASEC 
    29  #   -vbs toexec.vbs 
    30  #   -filter 'SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance 
    31  #            ISA "Win32_Process" AND TargetInstance.Name = "calc.exe"'
    32  #
    33  # or, if you just want to execute the VBS every XXX milliseconds:
    34  #
    35  #  wmipersist.py domain.net/adminuser:mypwd@targetHost install -name ASEC 
    36  #   -vbs toexec.vbs -timer XXX 
    37  #
    38  # to remove the event:
    39  #	wmipersist.py domain.net/adminuser:mypwd@targetHost remove -name ASEC
    40  #
    41  # if you don't specify the password, it will be asked by the script.
    42  # domain is optional.
    43  #
    44  # Reference for:
    45  #  DCOM/WMI
    46  from __future__ import division
    47  from __future__ import print_function
    48  import sys
    49  import argparse
    50  import logging
    51  
    52  from impacket.examples import logger
    53  from impacket import version
    54  from impacket.dcerpc.v5.dcomrt import DCOMConnection
    55  from impacket.dcerpc.v5.dcom import wmi
    56  from impacket.dcerpc.v5.dtypes import NULL
    57  
    58  
    59  class WMIPERSISTENCE:
    60      def __init__(self, username = '', password = '', domain = '', options= None):
    61          self.__username = username
    62          self.__password = password
    63          self.__domain = domain
    64          self.__options = options
    65          self.__lmhash = ''
    66          self.__nthash = ''
    67          if options.hashes is not None:
    68              self.__lmhash, self.__nthash = options.hashes.split(':')
    69  
    70      @staticmethod
    71      def checkError(banner, resp):
    72          if resp.GetCallStatus(0) != 0:
    73              logging.error('%s - ERROR (0x%x)' % (banner, resp.GetCallStatus(0)))
    74          else:
    75              logging.info('%s - OK' % banner)
    76  
    77      def run(self, addr):
    78          dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
    79                                options.aesKey, oxidResolver=False, doKerberos=options.k, kdcHost=options.dc_ip)
    80  
    81          iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
    82          iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
    83          iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
    84          iWbemLevel1Login.RemRelease()
    85  
    86          if self.__options.action.upper() == 'REMOVE':
    87              self.checkError('Removing ActiveScriptEventConsumer %s' % self.__options.name,
    88                              iWbemServices.DeleteInstance('ActiveScriptEventConsumer.Name="%s"' % self.__options.name))
    89  
    90              self.checkError('Removing EventFilter EF_%s' % self.__options.name,
    91                              iWbemServices.DeleteInstance('__EventFilter.Name="EF_%s"' % self.__options.name))
    92  
    93              self.checkError('Removing IntervalTimerInstruction TI_%s' % self.__options.name,
    94                              iWbemServices.DeleteInstance(
    95                                  '__IntervalTimerInstruction.TimerId="TI_%s"' % self.__options.name))
    96  
    97              self.checkError('Removing FilterToConsumerBinding %s' % self.__options.name,
    98                              iWbemServices.DeleteInstance(
    99                                  r'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"%s\"",'
   100                                  r'Filter="__EventFilter.Name=\"EF_%s\""' % (
   101                                  self.__options.name, self.__options.name)))
   102          else:
   103              activeScript ,_ = iWbemServices.GetObject('ActiveScriptEventConsumer')
   104              activeScript =  activeScript.SpawnInstance()
   105              activeScript.Name = self.__options.name
   106              activeScript.ScriptingEngine = 'VBScript'
   107              activeScript.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
   108              activeScript.ScriptText = options.vbs.read()
   109              self.checkError('Adding ActiveScriptEventConsumer %s'% self.__options.name, 
   110                  iWbemServices.PutInstance(activeScript.marshalMe()))
   111          
   112              if options.filter is not None:
   113                  eventFilter,_ = iWbemServices.GetObject('__EventFilter')
   114                  eventFilter =  eventFilter.SpawnInstance()
   115                  eventFilter.Name = 'EF_%s' % self.__options.name
   116                  eventFilter.CreatorSID =  [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
   117                  eventFilter.Query = options.filter
   118                  eventFilter.QueryLanguage = 'WQL'
   119                  eventFilter.EventNamespace = r'root\cimv2'
   120                  self.checkError('Adding EventFilter EF_%s'% self.__options.name, 
   121                      iWbemServices.PutInstance(eventFilter.marshalMe()))
   122  
   123              else:
   124                  wmiTimer, _ = iWbemServices.GetObject('__IntervalTimerInstruction')
   125                  wmiTimer = wmiTimer.SpawnInstance()
   126                  wmiTimer.TimerId = 'TI_%s' % self.__options.name
   127                  wmiTimer.IntervalBetweenEvents = int(self.__options.timer)
   128                  #wmiTimer.SkipIfPassed = False
   129                  self.checkError('Adding IntervalTimerInstruction',
   130                      iWbemServices.PutInstance(wmiTimer.marshalMe()))
   131  
   132                  eventFilter,_ = iWbemServices.GetObject('__EventFilter')
   133                  eventFilter =  eventFilter.SpawnInstance()
   134                  eventFilter.Name = 'EF_%s' % self.__options.name
   135                  eventFilter.CreatorSID =  [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
   136                  eventFilter.Query = 'select * from __TimerEvent where TimerID = "TI_%s" ' % self.__options.name
   137                  eventFilter.QueryLanguage = 'WQL'
   138                  eventFilter.EventNamespace = r'root\subscription'
   139                  self.checkError('Adding EventFilter EF_%s'% self.__options.name, 
   140                      iWbemServices.PutInstance(eventFilter.marshalMe()))
   141  
   142              filterBinding,_ = iWbemServices.GetObject('__FilterToConsumerBinding')
   143              filterBinding =  filterBinding.SpawnInstance()
   144              filterBinding.Filter = '__EventFilter.Name="EF_%s"' % self.__options.name
   145              filterBinding.Consumer = 'ActiveScriptEventConsumer.Name="%s"' % self.__options.name
   146              filterBinding.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
   147  
   148              self.checkError('Adding FilterToConsumerBinding',
   149                  iWbemServices.PutInstance(filterBinding.marshalMe()))
   150  
   151          dcom.disconnect()
   152  
   153  # Process command-line arguments.
   154  if __name__ == '__main__':
   155      # Init the example's logger theme
   156      logger.init()
   157      print(version.BANNER)
   158  
   159      parser = argparse.ArgumentParser(add_help = True, description = "Creates/Removes a WMI Event Consumer/Filter and "
   160                                 "link between both to execute Visual Basic based on the WQL filter or timer specified.")
   161  
   162      parser.add_argument('target', action='store', help='[domain/][username[:password]@]<address>')
   163      parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
   164      subparsers = parser.add_subparsers(help='actions', dest='action')
   165  
   166      # A start command
   167      install_parser = subparsers.add_parser('install', help='installs the wmi event consumer/filter')
   168      install_parser.add_argument('-name', action='store', required=True, help='event name')
   169      install_parser.add_argument('-vbs', type=argparse.FileType('r'), required=True, help='VBS filename containing the '
   170                                                                                           'script you want to run')
   171      install_parser.add_argument('-filter', action='store', required=False, help='the WQL filter string that will trigger'
   172                                                                                  ' the script')
   173      install_parser.add_argument('-timer', action='store', required=False, help='the amount of milliseconds after the'
   174                                                                                 ' script will be triggered')
   175  
   176      # A stop command
   177      remove_parser = subparsers.add_parser('remove', help='removes the wmi event consumer/filter')
   178      remove_parser.add_argument('-name', action='store', required=True, help='event name')
   179  
   180      group = parser.add_argument_group('authentication')
   181  
   182      group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
   183      group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
   184      group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
   185                         '(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
   186                         'ones specified in the command line')
   187      group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
   188                                                                              '(128 or 256 bits)')
   189      group.add_argument('-dc-ip', action='store',metavar = "ip address",  help='IP Address of the domain controller. If '
   190                         'ommited it use the domain part (FQDN) specified in the target parameter')
   191   
   192      if len(sys.argv)==1:
   193          parser.print_help()
   194          sys.exit(1)
   195  
   196      options = parser.parse_args()
   197  
   198          
   199      if options.debug is True:
   200          logging.getLogger().setLevel(logging.DEBUG)
   201      else:
   202          logging.getLogger().setLevel(logging.INFO)
   203  
   204  
   205      if options.action.upper() == 'INSTALL':
   206          if (options.filter is None and options.timer is None) or  (options.filter is not None and options.timer is not None):
   207              logging.error("You have to either specify -filter or -timer (and not both)")
   208              sys.exit(1)
   209  
   210      import re
   211  
   212      domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
   213          options.target).groups('')
   214  
   215      #In case the password contains '@'
   216      if '@' in address:
   217          password = password + '@' + address.rpartition('@')[0]
   218          address = address.rpartition('@')[2]
   219  
   220      try:
   221          if domain is None:
   222              domain = ''
   223  
   224          if options.aesKey is not None:
   225              options.k = True
   226  
   227          if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
   228              from getpass import getpass
   229              password = getpass("Password:")
   230  
   231          executer = WMIPERSISTENCE(username, password, domain, options)
   232          executer.run(address)
   233      except (Exception, KeyboardInterrupt) as e:
   234          if logging.getLogger().level == logging.DEBUG:
   235              import traceback
   236              traceback.print_exc()
   237          logging.error(e)
   238      sys.exit(0)