5 ms·
0. evil.com hosts evil.js, <script src=evil.js integrity=foo>. 1. you visit evil.com and the browser stores evil.js with the cache key "foo". 2. you visit vic
by bugmen0t 11y ago
0. evil.com hosts evil.js, <script src=evil.js integrity=foo>.
1. you visit evil.com and the browser stores evil.js with the cache key "foo".
2. you visit victim.com which has an XSS vulnerability, but victim.com thinks it is safe because it uses Content Security Policy and does not allow inline scripts or scripts form evil domains.
3. the XSS attack is loading <script src=www.victim.com/evil.js hash=foo>
4. the browser detects that "foo" is a known hash key and loads the evil.js from cache. Thinking that the file is hosted on victim.com - when the file is in fact not even present.
5. the evil.js script executes in the context of victim.com, even though they use a Content Security Policy to prevent XSS from being exploitable.
- sidarape 11y agoI see. Genuine question: I haven't thought this through but why not just execute the file in the context of the original file?
- icebraining 11y agoBecause if the file was legitimate, the site might need it to run under its own context.
- ukd1 11y agoIsn't the solution to only allow caching using the key if it's over https (to stop modification) AND in the original HTML (i.e. not added afterwards by JS). Limiting, but would cover above.
- icebraining 11y agoIf you can inject/alter data over the wire, all protections are usually moot - e.g. you can simply omit the CSP header and inject your code. Most attackers don't have that capacity, though; XSS is usually done by tricking the page into running your own JS code (for example, by finding a publicly editable text area which doesn't properly escape HTML). Those restrictions wouldn't stop this attack.
- im3w1l 11y agoThat's a clever attack. Possible solution: Have two tables hash -> file and hash -> "set of domains we have verified has the file". If victim.com uses a CSP, then we look in the second table. We see that so far we only know that evil.com has the file. We therefore request the www.victim.com/evil.js and hash it. If it matches, we add it to the set. If it doesn't we bail. EDIT: Although I guess the current URL based cache may already dedupe, in which case my solution would be roughly equivalent to just turning off hash-based caching for domains with CSP.
- AndrewDucker 11y agoThanks! The original poster said to also use the size. If you include that, my understanding is that crafting a hash collision is into the realms of impossibility. Am I wrong? =========== Turns out I was completely misunderstanding. I now do. Thanks!
- MichaelGG 11y agoThere is no hash collision happening.
- deathanatos 11y agoI'm not seeing how size would prevent the attack. If I understand bugmen0t correctly, in the attack outlined, the file "evil.js" size (and hash and contents) are completely controlled by the attacker. If you did need to specify size, the attack would simply change to: 3. the XSS attack is loading <script src=www.victim.com/evil.js hash=foo size=123> > If you include that, my understanding is that crafting a hash collision is into the realms of impossibility. Crafting a hash collision is already in the realm of impossibility. (They're using cryptographic hashes: if you can make SHA256 collide, we have bigger problems.) The attack here isn't that you're getting the wrong file, it's that you're getting a file the webserver does not have, at all. Step 4 is where we go wrong: we load the file from cache, while we should instead request it from the server, which will 404 the request because it does not have the file. (And it's JS: even if size did matter, you can just add spaces to the end…)
- AndrewDucker 11y agoSorry, my brain clearly imploded while I was reading that. On re-reading it makes perfect sense. Thanks!
- kuschku 11y agoHow about you'd do a request saying "hey, I’d like to have victim.com/evil.js, I have a file with hash 93987590837309, is that still up-to-date?", and then you’d hope for 304 Not Modified (and load from evil.com/evil.js), or you’d get a 404 or 200 or whatever.
- deleted 11y ago[deleted]
- leni536 11y agoWhat if at least a HEAD request is required?
- realusername 11y agoI still don't see how it's a problem either way. The browser should check that the hash matches before storing it into the cache so evil.com/evil.js has not the right hash so it's not stored. If they can craft a sha256 collision then, we have other problems anyway and sha256 should be deprecated. If the hashes are the same, then the files are the same.
- icebraining 11y agoNo, the attacker decides the hash, since they inject the <script> tag using XSS.
- clinta 11y agoThe hash would be correct. The JS file is the same. The key to the attack is: "the XSS attack is loading <script src=www.victim.com/evil.js hash=foo>". So victim.com was never hosting evil.js and never intended to serve it. The visitor to victim.com gets it because of an XSS vulnerability. victim.com should be protected because it's content security policy tells the browser not to run scripts from evil.com, but the browser thinks that evil.js came from victim.com, even though victim.com doesn't host evil.js and the browsers cache got evil.js from evil.com.
- moe 11y agoBut if the scenario is an attacker who can inject HTML tags, why wouldn't they simply run their script directly via <script>do_evil();</script>?
- pauljohncleary 11y agoBecause a properly configured content security policy will block any inlined js (and external js files on non whitelisted domains)
- CJefferson 11y agoThe problem is that www.victim.com/evil.js doesn't exist, and never did, but your browser won't know that if it is in it's cache -- this gives you a way of faking files existing on other servers at the URL of your choices, and as long as they are in the cache you'll get away with it.
- zimbatm 11y ago6. you just need a collision where evil.js would generate the same sha3sum "foo"
- auston 11y agoI agree with the guy that said the HEAD request. If you couple that with an "integrity-hash" header that is just like the attribute on the script tag than you can compare the hashes. What do you think are the downsides to something like that?
- clinta 11y agoThe browser could submit a special query to get the hash for victim.com/evil.js before running it. If victim.com returns the same hash, it's clean, if it doesn't respond, or responds with a different hash, fail in the same way as if a CDN had modified it.
- phaker 11y agoMaybe use ETag or a similar mechanism? This way we'll need to contact the server, but we save bandwidth that would be spent resending the resource, which is still faster than what we have. Given something like this: <script src="https://code.jquery.com/....." integrity="sha384-R4/....." shared> (where 'shared' invokes the caching mechanism) The browser sends a request for it with If-None-Match: "sha384-R4/....." header set. I think this solves 99% of the problem: If the integrity tag doesn't match the ETag of the resource, the server interprets it as out-of-date cache and responds with content of that resource. If the integrity tag matches the ETag of the resource, it will respond with '304 Not Modified'. And that's the remaining 1% of the attack surface: basically the attacker wins iff the site can be tricked into serving a resource with the same ETag as the hash of his payload. We don't need to worry about collisions: even if someone uses ETags that match the form of subresource integrity tags without intending to, the attacker would still need to generate a collision, which is just as hard as finding a collision with any other hash. But if there are servers out there that will serve files with externally-set ETags then they'd be exploitable.
- kuschku 11y agoAnd what if you add filesize checking? Just do a request and hope that the server returns you a 304 Not-Modified. This should prevent the issue, right?