Creds

Een vaak onopgemerkte XSS-vector

Scripts en comments zorgen voor een interessante dag.

Een van de expertisegebieden van CREDS is het testen van webapplicaties. We hebben een team dat zich uitsluitend richt op het testen van webapplicaties en onderzoek naar kwetsbaarheden in webapplicaties. Deze blogpost beschrijft een gelaagde Cross-Site Scripting aanval die vaak over het hoofd wordt gezien door pentesters.

Een tijdje geleden merkten we dat verschillende applicaties op een onverwachte manier karakters escapen tussen scripttags. Door mijn test <b>invoer </b> te sturen, kregen we de volgende HTML teruggestuurd:

<html>
<body>
<script>
    console.log("mijn test <b>invoer <\/b>");
</script>
</body>
</html>

Zoals je kunt zien vertaalt de applicatie de kleiner- en groter dan karakters (<,>) niet naar hun hex representatie (\x3c & \x3e). In plaats daarvan wordt / vertaald naar \/.

Dit stopt een klassieke aanvalsvector waarbij de payload </script><script>alert(1)</script> tussen scripttags wordt gestopt.

<html>
<body>
<script>
    console.log("mijn test </script><script>alert(1)</script>");
</script>
</body>
</html>

De bovenstaande klassieke aanvalsvector produceert een welbekende pop-up melding.

Er zijn gecompliceerde historische redenen die het gebruik van deze escaping populair hebben gemaakt en het wordt vaak gedaan zodat JavaScript deze waarden kan gebruiken zonder dat de HTML parser deze probeert te interpreteren en mogelijk de JavaScript breekt. Dit gebeurt omdat JavaScript \ en \/ als hetzelfde karakter beschouwt, maar de HTML parser niet.

Het escapen van enkel (/) naar \/ betekent echter niet dat de overige karkaters in script tags juist worden behandeld. En door het gebrek aan een goede sanitisatie kunnen we nog steeds HTML in het document injecteren onder een aantal (vrij gebruikelijke) voorwaarden.

De reden dat we nog steeds HTML in het document kunnen injecteren, is dat de HTML-parser niet alleen scant op de afsluitende scripttag (</script>), maar een extra set vreemde beperkingen heeft. Bijvoorbeeld na het openen van HTML commentaar (<!--) binnen een scripttag, begint hij alle volgende sets van script open en sluit tags te negeren totdat hij het sluiten van het HTML commentaar tegenkomt (-->).

Het sluiten van een scripttag in HTML-commentaar sluit nog steeds de vorige open scripttag.

Nu we dit gedrag kennen, kunnen we op zoek gaan naar een manier om het gebrek aan sanitization uit te buiten. Het blijkt dat we gewoon een locatie in het HTML-document nodig hebben ergens na de scripttag waar we invoer hebben en de <, >, - en / tekens en tekst kunnen invoeren.

Het blijkt dat nogal wat applicaties deze tekens niet escapen binnen HTML tag attributen.

Dus als we het volgende voorbeeld bekijken

<html>
<body>
<script>
    console.log("input_1");
</script>
...
<input type="text" value="input_2">
...
</body>
</html>

We kunnen HTML in het document injecteren door de eerste invoer in te stellen op <!--<script> en de tweede op --><script><b>html injected</b> waardoor de volgende html ontstaat:

<html>
<body>
<script>
    console.log("<!--<script>");
</script>
...
<input type="text" value="--></script><b>test</b>">
...
</body>
</html>

Merk op dat de set scripttags, ingesloten in de eerste set scripttags, genegeerd wordt door de HTML parser, waardoor de inhoud van de scripttag verandert in:

console.log("<!--<script>");
</script>
...
<input type="text" value="-->

Dit is duidelijk geen geldig JavaScript en geeft een syntaxfout.

Door te veranderen waar de scripttags eindigen, wordt een deel van het HTML-document uitgecommentarieerd, waardoor de structuur verandert. Omdat alles na de door de gebruiker ingevoerde --></script> nu wordt gezien als onderdeel van de HTML, zullen browsers het weergeven als:

Onopgemerkte XSS PoC

Dit betekent dat we een werkende HTML-injectie hebben. Nu is het triviaal om een cross site scripting-aanval uit te voeren door de tweede parameter te wijzigen in iets als --></script><script>alert(1)</script>.

Het resultaat is een HTML-document dat er als volgt uitziet.

<html>
<body>
<script>
    console.log("<!--<script>");
</script>
...
<input type="text" value="--></script><script>alert(1)</script>">
...
</body>
</html>

De resulterende HTML van de laatste aanval produceert een prachtig waarschuwingsvak.

Onopgemerkte XSS-waarschuwing

Toepassingen sturen steeds vaker dynamisch gegenereerde gegevens in scripttags, omdat dit voorkomt dat ze een extra HTTP-verzoek moeten doen om de gegevens op te halen. Door deze toename komt deze aanvalsvector steeds vaker voor.