12 ms·
An XSS on Facebook via PNGs and Wonky Content Types
- yvoschaap2 11y agoThe CDN to *.facebook.com find is really cool. May I ask what the bounty reward was for you?
- ottertown 11y agoMan, I work as a frontend engineer at one of the big tech companies and I understood like 25% of this post. And that's not meant to be a criticism, I'm just reflecting on my total ignorance of most security vulnerabilities. I know about and having implemented some measures against XSS / CSRF, but it's clear there are dozens of attack approaches I'm not even aware of. I feel like I have some homework to do.
- mettamage 11y agoWell, he's a security specialist. And I feel he's figuring out new ways to attack. If you're not an attacker yourself, then I think you'll always lag behind. I'm currently taking a security class and what I'm noticing is that I mistake the complicatedness for complexity but in essence most vulnerabilities and attacks have the same high level overview. What I'm trying to say is: don't feel so bad, if you understood 25%, then you know more than you think. Perhaps you too are captured by the complicatedness of his post ;)
- LeifCarrotson 11y ago> I mistake the complicatedness for complexity You conjugated the same word two ways. What do you mean?
- viraptor 11y agoI think he meant the difference between things being complicated (possibly unnecessarily) and complex (inherently). Simple things can be complicated to the level they're hard to understand, but it doesn't mean they are complex.
- mettamage 11y agoYes this is what I meant. Perhaps I should've given a bit more context in my original post. Here's a fun example. English has a lot of Latin and Germanic based words in it that you can construct entire sentences. I was surprised to find out as a non-native speaker. In my opinion, the Latin sentences seem way too complicated to express simple ideas. Germanic example: In my anger I struck my small sword in his belly. Latin example: In my rage I injected my gladius in his abdomen. More examples: http://corrinejackson.com/wordpress/2013/04/23/tuesday-writing-tips-anglo-saxon-vs-latinate-diction/ http://corrinejackson.com/wordpress/2013/04/23/tuesday-writi... http://www.antimoon.com/forum/t2068.htm http://www.antimoon.com/forum/t2068.htm
- saint_fiasco 11y agoComplexity is an adjective turned into a noun. Complex -> complexity On the other hand, complicatedness is a verb turned into an adjective turned into a noun. Complicate -> complicated -> complicatedness I read that as an implication that security concepts are not inherently difficult but are made difficult by people who explain it incorrectly.
- superswordfish 11y agoIt's interesting that you think working for a big tech company makes you especially qualified (or else you would not have mentioned it here). By working for a smaller company, you'd have to know these things or else face a world of pwnage. > I feel like I have some homework to do. Start with X-Frame-Options and HttpOnly cookies.
- danielhlockard 11y agoAnd Secure, if you're using HTTPS (which you should be)
- deleted 11y ago[deleted]
- airza 11y agoThe root problem is that something which shouldn't be HTML was sent back with the HTML mime type. Usually this happens with content type sniffing (IE no MIME type is specified) but it leaves the door open to attacks like these. It changes the handler of the input from code designed to care about security (IE your upload handler code) to code designed to care about usability (MIME sniffing heuristics, or in this case the decompression/rendering library). When this happens you usually get bad/unexpected results, but it makes it easy to figure out where you can stop caring about implementation details- when the user input leaves the area of the code designed to secure it!
- pyre 11y agoThe two major issues here: 1. Allowing something that is not HTML (and user-supplied) to be returned by the server with the text/html MIME type, causing the browser to want to parse it as HTML. 2. A link between the CDN (Akamai) domains and a top-level sub-domain on Facebook (i.e. photos.facebook.com was aliased to some Akamai domains). At this point the attacker is able to serve HTML from a Facebook domain. There are things that could be protected against here, but the attacker has a lot of vectors they can go for at this point.
- deleted 11y ago[deleted]
- yummybear 11y agoDon't worry. Facebook's engineers missed this one too.
- keeperofdakeys 11y agoOWASP can be a good guide https://www.owasp.org/index.php/Top_10_2013-Top_10 https://www.owasp.org/index.php/Top_10_2013-Top_10
- EGreg 11y agoThat ascii art is cool!! Is it a feature of the Akamai CDN?
- richdougherty 11y agoMain points for me: 1. Don't serve user supplied content from a subdomain of your main site. Facebook fixed this by removing the redirect from photo.facebook.com to their CDN, so now user content comes from *.akamaihd.net. 2. If a user uploads a PNG (or whatever), and you've validated it for that content type, don't serve it from your servers with a different content type. Clever users can make files that browsers will handle in different ways for different content types. Facebook's CDN still seems to allow serving files with the wrong (unvalidated) content type just by changing the file suffix in the URL.
- baby 11y ago> 1. Don't serve user supplied content from a subdomain of your main site. Facebook fixed this by removing the redirect from photo.facebook.com to their CDN, so now user content comes from *.akamaihd.net. I don't see that as an issue. As long as you don't allow for wildcard extensions on your server it won't return a wrong content-type in this kind of situation. I wonder why it's even possible that you can change the .jpg to .html :| I'm pretty sure this is disabled by default in apache. > don't serve it from your servers with a different content type. so yeah, the problem of the content-type comes from the fact that you can manipulate the extension as you wish once uploaded.
- update 11y ago> Facebook's CDN still seems to allow serving files with the wrong (unvalidated) content type just by changing the file suffix in the URL. Is this common practice amongst websites? seems like a weird config to have enabled.
- grrowl 11y agoIt is weird. I suppose as a result of storing file content by hash only (without extension), then determining the Content-Type header based on the user-supplied extension. Unless they're storing the original filename, they'll have to run through the binary data to determine file type. Crucially though, is they removed the DNS forward to the CDN. Which means they're actually storing .html files on those CDN servers. For ads, presumably, but it seems terribly silly to be storing .html files there at all.
- moray 11y agoThat reminded me the fun I had at uni (some time ago..) trying to exploit a buffer overflow vulnerability of an ftp server... for the security class...
- dsacco 11y agoExcellent writeup and congratulations to Jack (fin1te)! He's been a long time bug bounty hunter for Facebook and he's joining their Product Security team in three weeks.
- speedy_go 11y agoand they say CSP is useless...
- nly 11y agoI don't think CSP could have prevented this? Facebook.com is already CSP enabled, but CSP only prevents cross-origin injection of certain resource types (images, scripts, Javascript). This attack originated from Facebooks own domain and was served (well, sniffed) as HTML. Anyway, CSP clearly isn't useless... but try deploying it on your average Wordpress blog sometime.
- yeukhon 11y ago> CSP only prevents cross-origin injection of certain resource types (images, scripts, Javascript). Are you sure ONLY? Enable CSP on a XSS-vulnerable website, send a payload, CSP prevents XSS from executing if your policy say so.
- seanwilson 11y ago> Anyway, CSP clearly isn't useless... but try deploying it on your average Wordpress blog sometime. What issues have you found with this?
- sangnoir 11y agoNot OP, but my guess is random plugins stop working with strict (i.e. secure) policies, if they weren't written with CSP in mind.
- seanwilson 11y agoBad for development time but good for security then I'd guess.
- nly 11y agoIts not really plugins. The vast majority of wordpress themes out there come with inline css, inline javascript, and prebaked dependencies on things like jquery and google fonts hosted on 3rd party CDNs.
- PeCaN 11y agoThat is incredibly clever, particularly exploiting DEFLATE to generate text. I would've never thought of that.
- viraptor 11y agoThey didn't use DEFLATE to generate the text. They used a known text and prepended garbage to it until it DEFLATEd correctly (to any result). Small difference, but still.
- deathanatos 11y agoThe author seems aware that it is possible, however: > Rather than trying to create this by hand, I used a brute-force solution (I’m sure there are much better ways, but I wanted to whip up a script and leave it running): He just didn't want to learn DEFLATE right then, right there. He got the same result. FWIW, I don't think this is actually hard. DEFLATE has a small header that tells you want type of compressed data follows; one of the types is "literally, this data" which is up to 64KiB long. (i.e., a plenty long enough string to get the job done.)
- nh2 11y agoDoesn't this XSS constitute a complete compromise of all Facebook user data, globally? (Assuming it had been found by a "bad guy", instead of a white-hat researcher.) Because: * He could execute arbitrary code in the browser of any user that views his picture * Including uploading all private photos and chats of that user to somewhere else * Or changing their password (assuming 2FA is not used) * He could have propagated this exploit to all Facebook users by simply uploading his XSS picture into his victim's profile, and from there into second-degree victims, and so on? According to the "6 degrees of separation" theory, this would have been very fast Or am I missing something?
- grrowl 11y agoHe couldn't have just "uploaded" images to a page, since facebook would output the original path (B1GL0N6H45H.png), where this relies on the .html extension. Easy enough to link people through Messenger or another site to that page, though. The core vector is the fact it's served off the photos.facebook.com domain.
- caf 11y agoA Facebook worm!
- comex 11y agoHmm, I wonder if there is anything interesting you could do by creating a Service Worker for the CDN domain. It seems that the CDN will serve arbitrary files with a text/javascript Content-Type, which is the requirement to set one up. Once created, AFAIK, such a worker remains attached to the domain until an occasional attempt by the browser to refresh it fails, and it can intercept and modify any requests from webpages on its origin, as well as navigation there. But it doesn't affect requests to that origin from other origins, like the typical <img src> from facebook.com, so it won't be able to do much. It seems that you click "download" on Facebook images, you'll get redirected to a CDN URL. So there's that. You could see what images the user is trying to download and edit them in flight... not a big deal, but it's something.
- joev_ 11y agoServiceWorkers are scoped to the basename of the path they are served from, so you could only intercept a small subset of all possible CDN URLs. In the example, this would be resources under `/hads-ak-xat1/t45.1600-2/`. But you probably can't create a ServiceWorker here anyways as you need a route that returns valid Javascript of your choosing with a Content-Type `text/javascript`. Usually when I've seen an opportunity for ServiceWorker exploits it's due to a JSONP endpoint that does not sanitize its callback parameter. If you could create a valid ServiceWorker at this route, and load it inside some same-domain HTML page, then you could theoretically use it to intercept and rewrite responses to any resources on the domain under its path. Depending on how you rewrite the response you might be able to get the browser to cache the rewritten resource, which could then get used by other domains. Theoretically. I have never tested this :)
- comex 11y agoThe default scope is the basename, but you can customize it, can't you? As I said, the text/javascript Content-Type is another one the CDN is willing to serve if you change the extension. I'm silly and didn't think of the fact that unlike HTML, a JS file can't be random binary garbage with a payload embedded somewhere in the middle. However, I am not sure that some allowed image format doesn't allow putting, say, // close enough to the beginning of the file that it will work. It seems like it might be possible with the JPEG header, but of course it depends on the post processing done. (Or maybe there is some way to upload non-images?)
- Gratsby 11y agoWow. I hope he alerted facebook before posting the blog entry.
- SixSigma 11y agoFrom TFA Fix Facebook quickly hot-fixed the issue by removing the forward DNS entry for photo.facebook.com. https://fin1te.net/images/facebookxss/facebook-xss-12.png https://fin1te.net/images/facebookxss/facebook-xss-12.png
- plugnburn 11y agoWow. Facebook had managed to fail on a thing absent in much less popular sites since the day one. A CDN absolutely mustn't allow a user to browse to a file with a different extension than it was uploaded with. It must return 404 in this case. II think the flaw is much more severe than has been discovered.
- bwindels 11y agoPretty clever, but I'm wondering how this would be exploited. To insert an XSS attack on facebook.com, he would have to be able to change the extension in url in the facebook page of an uploaded image to .html, right? Don't see a way of doing that. Am I missing something, or the attack assumes you can convince a victim to go to the given URL through other means? Like spreading through IM, E-mail...
- bwindels 11y agoAfter reading the comments here, it seems like the attack would indeed assume you spread the alternative URL with the .html extension through other means like IM, ...
- dwild 11y agoThere's multiple way to spread it, you put that url inside an iframe and then you could upload it on some ad network, you could build some viral content somewhere and let it spread that way. It's pretty easy to spread an XSS.