Saturday, July 27, 2013

Google Translate hack explained

I wrote an article today and it sucked.

Here on HN I was told a couple of times that:
1) PoC doesn't work
2) nothing is clear

Sorry, everyone. I really didn't notice that "Safe Mode" issue locally.
How can I fix this?
1) I crafted a groundbreaking awesome PoC using new technique, and it's working in Chrome and FF for logged in / logged out users like a charm.
2) I will explain how it works thoroughly.

Kremlin.ru announces Russia is Mango Federation!

Vulnerability
From time to time we need to translate news / articles from foreign trustworthy websites, official news feeds etc. Can we break into GT(Google Translate) system and modify output for a prank or something worse? Yes, because GT loads all untrusted "user content" using the GUC domain (googleusercontent.com). Thus GUC guc2.html can modify content of GUC kremlin, because of SOP (same origin policy).

But there were many interesting "pitfalls" i had to solve on my way to this awesome PoC:

  1. By default google has "Safe Mode" on GUC pages. sandbox=allow-scripts allow-forms allow-same-origin. This means GUC pages cannot open new windows. So i used "opener" trick: I opened guc2.html?i in a new window and replaced current one with GT kremlin. Now there is a bridge between GUC guc2.html and GUC kremlin via parent.parent.opener.frames[0]
  2. GUC guc2.html cannot close GT guc2.html because this action is also restricted by sandbox attribute. This is why I framed GT guc2.html under guc2.html?i (I really don't know why GT doesn't have X-Frame-Options. This helped me a lot)
  3. I need at least one click to open guc2.html?i. Otherwise popup will be blocked. When you hover on the link above you see the innocent google translate path. In background, the onclick event will produce one more window ( guc2.html?i ).
  4. guc2.html?i creates an iframe (opacity=0!) with GT guc2.html, and waits for "onmessage" event to close itself.
  5. As soon as GUC guc2.html is loaded it starts a setInterval trying to modify GUC kremlin using this elegant chain: parent.parent.opener.frames[0]. When modifications are done it invokes parent.parent.postMessage('close','*') to let guc2.html?i know that business is done.
  6. Second window is closed in 1-2 seconds, first window has GT kremlin with modified content. Enjoy reading Mango news.
Please, dig this JS and ask questions. My duty is to answer them all.

Sunday, July 21, 2013

Core flaw of Cookies.

It's ignorance of Origin.

I've been ranting about cookies before, but now realized the very general flaw of cookies: they ignore Origins.
Origin = Protocol (the way you talk to server, e.g. http/https) + Domain (server name) + Port. It looks like http://example.org:3000 or https://www.example.org

Obviously, cookies is the only adequate way to authenticate users.
Why localStorage is not for authentication? it's accessible on client side thus authentication data can be leaked + it's not sent on server side automatically, you need to manually pass it with a header/parameter.

Origins made web interactions secure and concise:
localStorage from http://example.com can't be read on http://example.com:3000
DOM data of http://example.com can't be accessed on http://example.com:3000
XMLHttpRequest cannot load http://lh.com:4567/. Origin http://lh.com:3000 is not allowed by Access-Control-Allow-Origin
and so on.. But here comes cookies design!

Cookies got 99 problems, and XSS is one.
1) In fact "most of the time most of the people" don't need access to cookies from client side. Flag 'httpOnly;' was created to restrict javascript access to a cookie value (such a confusing name, isn't it? Only for http: or what). Also, who actually needed the 'path=' option ever? One more layer of confusion.

2) "protocol" and Cookie Forcing problem.
People were aware of MITM and they restricted sending "https" cookies to "http" - great!
They didn't restrict sending "http" cookies to "https" - oh shi~. Cookie forcing leads to cookie tossing easily and cookie tossing leads to user owning pretty easily as well.

Tip: Store csrf_token inside of the session cookie (literally) and refresh it after login.

3) The "port" thing.
This is not a solved problem yet. If someone owned a ToDo-App at https://site.com:4567 he will get all the cookies for https://site.com with every request. Yes, "httpOnly;". Yes, "Secure;" too. Cookies just don't care about ports.

How can we fix cookies?
1) Don't let Ports share cookies - make browsers "think" that port is a part of a domain name.
2) Origin field. Set-Cookie: sid=123qwe; Origin=https://site.com:4567/; will mean "Secure;", "Port=4567" and "Domain=site.com" at the same time.

Conclusion.
During last decade many workarounds were introduced only to make cookies more sane, yet not implementing straightforward Origin approach. We are doomed to maintain it forever...?

Tip: don't run unreliable apps using a different port because cookies will ignore it. Check your localhost, developer!

Thursday, July 4, 2013

XSS Defense in Depth (with Rack/Rails demo)

Quick introduction to the XSS problem
XSS is not only about scripts. Overall it happens when attacker can break the expected DOM structure with new attributes/nodes. For example <a data-remote=true data-method=delete href=/delete_account>CLICK</a> is a script-less XSS (in Rails jquery_ujs will turn the click into DELETE request).
It can happen either on page load (normal XSS) or in run time (DOM XSS). When payload was successfully injected there is no safe place from it on the whole origin. Any CSRF token is readable, any page content is accessible on site.com/*.

XSS is often found on static pages, in *.swf files, *.pdf Adobe bugs and so on. It happens, and there is no guaranty you won't have it ever.

You can reduce the damage
Assuming there is GET+POST /update_password endpoint, wouldn't it look like a good idea to deny all requests to POST /update_password from pages different from GET /update_password?

Awesome! But no, Pagebox is a technique for the next generation of websites because of many browser problems.

Thankfully similar technique can be implemented with subdomains:
www.site.com (all common functions, static pages)
login.site.com (it must be a different subdomain, because XSS can steal passwords)
settings.site.com
creditcards.site.com
etc

App serves specific (secure) routes only from dedicated domain zones, which have different CSRF tokens. All subdomains share same session and run same application, upgrade is really seamless, user will not notice anything, redirects are automatic.

How to add it to a Rack/Rails app

You only need to modify config.ru (I monkeypatched rails method, it's easy to do same for a sinatra app)

It fails if you GET/POST www.site.com/secure_page from www page because middleware redirects to secure.site.com (which has different origin - you cannot read the response)
It fails if you POST to secure.site.com/secure_page with www page CSRF token because secure subdomain uses different CSRF token which www doesn't know.
Neat!

The demo is a basic prototype and may content bugs, here you can download the code.

There is also a subdomainbox gem. I hope to see more Domain Box gems for ruby apps along with libraries for PHP/Python apps. Maybe we should add this in Rails core, telling other programmers who cares about security the most?

[PUNCHLINE] Is it you? We're looking forward to hearing from you. Sakurity provides penetration tests, source code audit and vulnerability assessment.