XSS Lab 20
Skills
- Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este laboratorio
contiene una vulnerabilidad stored XSS
en la funcionalidad de comentarios
. Para resolver
este laboratorio, debemos enviar un comentario
que llame a la función alert()
cuando se haga clic en el nombre del autor
del comentario
. Esto implica inyectar código malicioso
en el campo correspondiente al nombre
del autor
, de manera que se ejecute
cuando un usuario interactúe
con ese elemento
Resolución
Al acceder
a la web
nos sale esto
Si pulsamos sobre View post
vemos una sección en la cual podemos comentar
Si rellenamos
el formulario
, lo enviamos
y nos abrimos
el código fuente
de la web vemos esto
He intentado cerrar la '
pero no ha sido posible porque me la escapa
con \
Si intentamos escapar
la \
nos la escapa
y por lo tanto no se interpreta
También nos escapa las /
, las "
y nos HTML encodea
en los <>
, por lo tanto la única manera que nos queda es HTML encodear
nosotros el payload
a enviar para ver si el desarrollador no ha tenido esto en cuenta. Hay varias formas
de HTML encodear
, la primera es la de codificación de entidades HTML
y la segunda es la codificación numérica
, de la cual hay varios tipos
1
http://foo?'-alert(1)-'
1
http://foo/?'-alert(1)-'
Para saber como funciona esta vulnerabilidad
me he creado un pequeño laboratorio
en local
, lo primero es abrirnos
nuestro editor
de código
y crear
un archivo
llamado index.html
con el siguiente contenido
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ejemplo de HTML Encode y URI Decode</title>
<script>
// Función para HTML decodear el input
function htmlDecode(str) {
var element = document.createElement('div');
if (str) {
element.innerHTML = str;
str = element.innerText || element.textContent;
}
return str;
}
// Función para URI decodear el input
function uriDecode(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str; // Si hay un error de decodificación, devuelve el valor original
}
}
// Función para manejar el formulario y actualizar los atributos
function updateLink() {
// Obtener el input del usuario
let userInput = document.getElementById('userInput').value;
// Primero, decodificar el input de entidades HTML
let decodedHtmlInput = htmlDecode(userInput);
// Luego, decodificar posibles componentes URI
let decodedUriInput = uriDecode(decodedHtmlInput);
// Inyectar el input decodificado en los atributos href y onclick
document.getElementById('author').href = "http://foo?" + decodedUriInput;
document.getElementById('author').setAttribute('onclick', "var tracker={track(){}};tracker.track('http://foo-" + decodedUriInput + "');");
}
</script>
</head>
<body>
<h1>Formulario de Entrada</h1>
<label for="userInput">Ingresa algo:</label>
<input type="text" id="userInput" placeholder="Escribe algo...">
<button onclick="updateLink()">Actualizar Link</button>
<p><a id="author" href="http://foo?'-alert(1)-'" onclick="var tracker={track(){}};tracker.track('http://foo?'-alert(1)-'');">test</a></p>
</body>
</html>
Posteriormente en el mismo directorio
que ese archivo nos montamos un servidor http
, en mi caso lo hago con python
1
# python -m http.server 80
Una vez hecho esto, en el navegador
debemos acceder
a nuestro localhost
. Cuando lo hagamos veremos esto
Si introducimos
este payload '-alert(1)-'
y nos abrimos
el inpector
de Chrome vemos
que se nos decodea
el HTML
Si pinchamos
sobre el link
se nos ejecutará
el código JavaScript
Si modificamos
el código
y ahora HTML encodeamos
el input
del usuario
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ejemplo de HTML Encode y URI Decode</title>
<script>
// Función para HTML encodear el input
function htmlEncode(str) {
return str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Función para URI decodear el input
function uriDecode(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str; // Si hay un error de decodificación, devuelve el valor original
}
}
// Función para manejar el formulario y actualizar los atributos
function updateLink() {
// Obtener el input del usuario
let userInput = document.getElementById('userInput').value;
// Primero, HTML encodear el input del usuario
let encodedHtmlInput = htmlEncode(userInput);
// Luego, decodificar posibles componentes URI (si es necesario)
let decodedUriInput = uriDecode(encodedHtmlInput);
// Inyectar el input HTML encodeado y decodificado en los atributos href y onclick
document.getElementById('author').href = "http://foo?" + decodedUriInput;
document.getElementById('author').setAttribute('onclick', "var tracker={track(){}};tracker.track('http://foo-" + decodedUriInput + "');");
}
</script>
</head>
<body>
<h1>Formulario de Entrada</h1>
<label for="userInput">Ingresa algo:</label>
<input type="text" id="userInput" placeholder="Escribe algo...">
<button onclick="updateLink()">Actualizar Link</button>
<p><a id="author" href="http://foo?'-alert(1)-'" onclick="var tracker={track(){}};tracker.track('http://foo?'-alert(1)-'');">test</a></p>
</body>
</html>
Si volvemos a introducir
el payload '-alert(1)-'
vemos que se está HTML encodeando
nuestro input
, por lo tanto, al web lo toma como si fuera texto
y nos imposibilita
la inyección
de código JavaScript
En el laboratorio Portswigger
se están escapando
y HTML encodeando
varios caracteres
, pero no está HTML encodeando
el input completo
, solo
ciertos caracteres específicos
. Por lo tanto, podemos intentar HTML encodear
nuestro payload
para que no detecte
los caracteres
y no
los escape
o HTML encodee
. De esta forma cuando el payload encodeado
se carga
se decodea automáticamente
por navegador
y es interpretado
. Esto lo podemos comprobar introduciendo
el payload '-alert(1)-'
en el campo website
a la hora de publicar
un comentario
Si nos fijamos en el código fuente
vemos que el input
que introducimos
está HTML encodeado
, esto es porque este texto
se carga
del servidor
y por lo tanto no es decodeado
Sin embargo, si nos abrimos el inpector
de Chrome
veremos que si está decodeado
, esto es porque es el navegador el que lo HTML decodea
. Por esto es importante HTML encodear
el input
del usuario
, para que cuando el navegador cargue
el contenido
del servidor
y lo decodee
lo interprete
como texto