XSS Lab 12
Skills
- Reflected DOM XSS
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este laboratorio
tiene una vulnerabilidad Reflected DOM XSS
. Estas vulnerabilidades
ocurren cuando la aplicación
del lado
del servidor
procesa datos de una solicitud
y refleja
esos datos
en la respuesta
. Luego, un script
en la página procesa
los datos
reflejados
de una manera insegura
, escribiéndolos finalmente en un sink
o función peligrosa
. Para resolver
este laboratorio, debemos explotar un XSS
y llamar a la función alert()
Resolución
Al acceder
a la web
nos sale esto
Mediante el DOM Invader
de Burpsuite
vamos a analizar
posibles vulnerabilidades
del DOM
Nos abrimos
el inspector
de Chrome
y nos dirigimos al DOM Invader
Copiamos
el payload yc5diz6f
y lo pegamos
en el cuadro
de búsqueda
, como ha encontrado un sink (función peligrosa)
nos lo muestra
Si pulsamos sobre Exploit
intentará explotar
el sink
, pero en este caso no ha logrado explotarlo
Para ver donde
ha encontrado
el sink
debemos pulsar
sobre Stack Trace
y dirigirnos a Console
Si pinchamos
sobre el enlace
nos llevará a un archivo JavaScript
, pero yo prefiero verlo directamente
en el navegador
, así que accedo a https://0ad20009048ecacd82f44c1e00fd0025.web-security-academy.net/resources/js/searchResults.js
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Definimos la función 'search' que recibe un parámetro 'path' (que sería la URL para realizar la búsqueda)
function search(path) {
// Creamos una nueva solicitud XMLHttpRequest para realizar la solicitud HTTP
var xhr = new XMLHttpRequest();
// Definimos el manejador del evento 'onreadystatechange' para procesar la respuesta de la solicitud
xhr.onreadystatechange = function() {
// Verificamos si la solicitud ha finalizado (readyState == 4) y si el código de estado es 200 (OK)
if (this.readyState == 4 && this.status == 200) {
// Usamos 'eval' para convertir la respuesta JSON en un objeto de JavaScript
eval('var searchResultsObj = ' + this.responseText);
// Llamamos a la función para mostrar los resultados de la búsqueda
displaySearchResults(searchResultsObj);
}
};
// Abrimos la solicitud GET, añadiendo los parámetros de búsqueda de la URL (window.location.search)
xhr.open("GET", path + window.location.search);
// Enviamos la solicitud
xhr.send();
// Función interna que maneja la visualización de los resultados de la búsqueda
function displaySearchResults(searchResultsObj) {
// Accedemos a los elementos del DOM donde se mostrarán los resultados
var blogHeader = document.getElementsByClassName("blog-header")[0];
var blogList = document.getElementsByClassName("blog-list")[0];
// Obtenemos el término de búsqueda y los resultados del objeto
var searchTerm = searchResultsObj.searchTerm;
var searchResults = searchResultsObj.results;
// Creamos un encabezado con el número de resultados encontrados
var h1 = document.createElement("h1");
h1.innerText = searchResults.length + " search results for '" + searchTerm + "'";
blogHeader.appendChild(h1);
// Agregamos una línea horizontal para separar el encabezado de los resultados
var hr = document.createElement("hr");
blogHeader.appendChild(hr);
// Iteramos sobre los resultados de la búsqueda y los mostramos
for (var i = 0; i < searchResults.length; ++i) {
var searchResult = searchResults[i];
// Si el resultado tiene un 'id', creamos un enlace a la publicación
if (searchResult.id) {
var blogLink = document.createElement("a");
blogLink.setAttribute("href", "/post?postId=" + searchResult.id);
// Si el resultado tiene una imagen de encabezado, la añadimos al enlace
if (searchResult.headerImage) {
var headerImage = document.createElement("img");
headerImage.setAttribute("src", "/image/" + searchResult.headerImage);
blogLink.appendChild(headerImage);
}
// Añadimos el enlace al listado de blogs
blogList.appendChild(blogLink);
}
// Agregamos un salto de línea entre los resultados
blogList.innerHTML += "<br/>";
// Si el resultado tiene un título, lo mostramos
if (searchResult.title) {
var title = document.createElement("h2");
title.innerText = searchResult.title;
blogList.appendChild(title);
}
// Si el resultado tiene un resumen, lo mostramos
if (searchResult.summary) {
var summary = document.createElement("p");
summary.innerText = searchResult.summary;
blogList.appendChild(summary);
}
// Si el resultado tiene un 'id', creamos un botón para ver la publicación
if (searchResult.id) {
var viewPostButton = document.createElement("a");
viewPostButton.setAttribute("class", "button is-small");
viewPostButton.setAttribute("href", "/post?postId=" + searchResult.id);
viewPostButton.innerText = "View post";
// Pero el botón no se agrega a ningún lado, falta un appendChild aquí.
}
}
// Creamos un enlace para volver al blog principal
var linkback = document.createElement("div");
linkback.setAttribute("class", "is-linkback");
var backToBlog = document.createElement("a");
backToBlog.setAttribute("href", "/");
backToBlog.innerText = "Back to Blog";
linkback.appendChild(backToBlog);
// Añadimos el enlace de vuelta al blog al final de la lista de blogs
blogList.appendChild(linkback);
}
}
El DOM Invader
está señalando a la función eval()
https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/eval. En esta web https://js.do/ podemos probar como funciona la función eval()
. Esta función evalúa
el contenido
de izquierda
a derecha
, por lo tanto si
se produce
un error antes
del payload
que queramos, no se ejecutará
, pero si se produce después, nuestro payload
seguirá pudiéndose ejecutar independientemente
de si hay errores
Vamos a ver como se está enviando la petición
, para ello ponemos lo que sea en el cuadro
de búsqueda
y capturamos
la petición
con Burpsuite
Si pulsamos
sobre Forward
recibiremos esta otra petición
Si la enviamos
al repeater
y vemos que la respuesta
que nos devuelve
es un JSON
Vamos a intentar escapar
del JSON
, si ponemos una comilla
nos añade una \
para escapar nuestra comilla
he invalidar
nuestro input
Hemos conseguido cerrar
la comilla
, añadiendo la \
, la cual escapa a la \
que nos añade
la web
Vamos a intentar llamar a alert()
aprovechando el funcionamiento de eval()
mediante este payload \"-alert(3)}//
. El símbolo -
sirve como concatenación
al string
de JSON
donde nosotros somos capaces de introducir
los datos
, es igual que +
, ambos son operadores aritméticos
que se utilizan para separar
la función alert()
de las expresiones anteriores
y comentamos
el resto
del JSON
con la //
Para explotar
esta vulnerabilidad
debemos introducir
este payload \"-alert(3)}//
en el cuadro
de búsqueda
Es posible explotar
esta vulnerabilidad
debido a que a la función eval('var searchResultsObj = ' + this.responseText);
le estamos pasando un string
y no un objeto JSON
, incluso aunque le pasáramos un objeto
de JavaScript
, seguiría siendo vulnerable
debido a que podríamos seguir pasándole una función
. Para que esto no pase debemos usar JSON.parse()
para convertir
el string
a un objeto JSON
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse. Aunque un objeto
de JavaScript
y un objeto JSON
son parecidos existen diferencias entre ambos, estas diferencias las podemos consultar en w3schools
https://www.w3schools.com/js/js_json_syntax.asp