File type • Content-Type (a.k.a. MIME type) identifies the file type • Browsers will determine how to interpret the file based on the Content-Type header • In this case, the file is served with text/plain • …which represents a plain text document 6
MIME Sniffing • If the Content-Type for a file is unrecognised, browsers will try to fix it with MIME Sniffing • …which determines the file type by inspecting the content with different algorithms (e.g. Magic Number) • But text/plain is a perfectly recognisable MIME type, how do we activate MIME Sniffing in this case?
MIME Sniffing & IE • Supposedly, the Content-Type header takes the highest precedence according to the specs • However, older versions of Internet Explorer will first guess the file type by performing MIME Sniffing • If a HTML sequence is found within the first 256 bytes, then the file is parsed as HTML 9
Anti-Sniffing • Turns out the header X-Content-Type-Options: nosniff is also served with the file • This header tells browsers not to perform MIME Sniffing • Now Internet Explorer plays by the rules 12
Fuzzing file extension • The upload endpoint only accept certain file types • Namely, CSV file (.csv) and TXT file (.txt) • We could try different file extensions and hopefully the validation is based on a blacklist • The idea is that, even though we cannot upload HTML files, there are other file types that can execute JavaScript 13
The return of MIME Sniffing • Unrecognised extensions are accepted • No mapping for the corresponding MIME type • No Content-Type for the file • MIME Sniffing is then activated on browsers to determine the file type • X-Content-Type-Options: nosniff is ignored in this case 15
Not so fast • ton.twitter.com hosts only static files, no damage can be done & • Files on ton.twitter.com can only be read by the uploader (Self-XSS) 20
Ideas of making it harmful • Find a page on the main domain (twitter.com) which sets document.domain="twitter.com" • An old technique for sub/parent domain communication • ❌ No such page was found • Steal session id • The session cookie is set on *.twitter.com! • ❌ Cannot read due to httpOnly • Control CSRF token •
Observation • _twitter_sess is accessible to subdomains • This cookie is used to store all kinds of miscellaneous data, but not readable due to httpOnly • However, this cookie is not bound to the session (session ID is stored in a separate cookie) • Could we replace victim’s _twitter_sess with ours, and force victim to use our CSRF token? 23
Cookie Tossing • Cookie key consists of the tuple (name, domain, path) • Subdomain can set cookies for parent domain and the effective path • foo is a different cookie from foo; path=/api. Their cookie flags are also separate • However, the Cookie header only contains the name and value for each of the cookies • Server will see two cookies with the same name 27
Cookie Precedence • Specs don’t tell servers how to handle duplicated cookies • Usually the first occurrence is picked • But how do browsers arrange their order? 29
Side-wide CSRF • Most of the APIs on Twitter are under the path /i/ • e.g. https://twitter.com/i/tweet/create, https://twitter.com/i/ user/follow • Running the below code snippet on ton.twitter.com… • document.cookie = '_twitter_sess=ATTACKER_SESS; domain=.twitter.com; path=/i' • …overrides (shadows) victim's CSRF token! 32
Not so fast • ton.twitter.com hosts only static files, no damage can be done ✅ • Files on ton.twitter.com can only be read by the uploader (Self-XSS) & 33
Ideas of getting rid of the “self-” • Check if the upload endpoint is vulnerable to CSRF • ❌No dice • Social Engineering • Ask the victim to upload our crafted file • ❌Infeasible • Login & Logout CSRF •
Attack flow 1. Logout victim’s session 2. Login to attacker’s account 3. Navigate victim to the XSS file 4. Override and fixate CSRF token 5. Logout attacker’s account 6. Next time victim logs in, attacker’s CSRF token will be used 35
Can we do better? • Now the attack is feasible, but • …victim needs to re-login • Victim also needs to visit attacker’s website again to trigger the CSRF • Can we authorise a request without involving session? 36
One step away • The file can now be accessed without a session • However, we need browsers to attach the OAuth parameter in the Authorisation header • There is no way to do it without violating SOP, at least for navigation • Dead end? Let’s check the spec… 40
Putting everything together 1. Upload a HTML file with an unknown extension 2. Attach OAuth parameter to the file URL 3. Make victim visit the file 4. The payload uses Cookie Tossing to override and fixate victim’s CSRF token 5. The payload performs actions on victim’s behalf 45
What went wrong • File upload should validate the file type using a white-list filter • Static files should be served from a separate sandboxed domain • CSRF token should be bound to user’s session 46
Mitigating Cookie Tossing & stuff • Drop duplicated cookies • GitHub’s approach • Cookie Prefixes • Currently only available on Chrome • If a cookie name begins with __Host-, the cookie cannot be accessed/modified from subdomains • Same-Site Cookies • Also available on Chrome only • Cookies with the SameSite flag will not be included the the request is issued cross- origin • The “ultimate” CSRF killer 48