Entrada

XSS Lab 12

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

Esta entrada está licenciada bajo CC BY 4.0 por el autor.