Entrada

XSS Lab 13

XSS Lab 13

Skills

  • Stored DOM XSS

Certificaciones

  • eWPT
  • eWPTXv2
  • OSWE
  • BSCP

Descripción

Este laboratorio demuestra una vulnerabilidad de Stored DOM XSS en la funcionalidad de comentarios del blog. Para resolver este laboratorio, debemos explotar esta vulnerabilidad y llamar a la función alert()


Resolución

Al acceder a la web nos sale esto

Si pinchamos sobre View post vemos una sección en la que podemos hacer comentarios

Si nos abrimos el inspector de Chrome vemos que hay un script

Accediendo a https://0a8d00c003e3219481205c76008f00e5.web-security-academy.net/resources/js/loadCommentsWithVulnerableEscapeHtml.js podemos ver el script, la parte más interesante es la función escapeHTML(html)

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
function loadComments(postCommentPath) {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            let comments = JSON.parse(this.responseText);
            displayComments(comments);
        }
    };
    xhr.open("GET", postCommentPath + window.location.search);
    xhr.send();

    function escapeHTML(html) {
        return html.replace('<', '&lt;').replace('>', '&gt;');
    }

    function displayComments(comments) {
        let userComments = document.getElementById("user-comments");

        for (let i = 0; i < comments.length; ++i)
        {
            comment = comments[i];
            let commentSection = document.createElement("section");
            commentSection.setAttribute("class", "comment");

            let firstPElement = document.createElement("p");

            let avatarImgElement = document.createElement("img");
            avatarImgElement.setAttribute("class", "avatar");
            avatarImgElement.setAttribute("src", comment.avatar ? escapeHTML(comment.avatar) : "/resources/images/avatarDefault.svg");

            if (comment.author) {
                if (comment.website) {
                    let websiteElement = document.createElement("a");
                    websiteElement.setAttribute("id", "author");
                    websiteElement.setAttribute("href", comment.website);
                    firstPElement.appendChild(websiteElement)
                }

                let newInnerHtml = firstPElement.innerHTML + escapeHTML(comment.author)
                firstPElement.innerHTML = newInnerHtml
            }

            if (comment.date) {
                let dateObj = new Date(comment.date)
                let month = '' + (dateObj.getMonth() + 1);
                let day = '' + dateObj.getDate();
                let year = dateObj.getFullYear();

                if (month.length < 2)
                    month = '0' + month;
                if (day.length < 2)
                    day = '0' + day;

                dateStr = [day, month, year].join('-');

                let newInnerHtml = firstPElement.innerHTML + " | " + dateStr
                firstPElement.innerHTML = newInnerHtml
            }

            firstPElement.appendChild(avatarImgElement);

            commentSection.appendChild(firstPElement);

            if (comment.body) {
                let commentBodyPElement = document.createElement("p");
                commentBodyPElement.innerHTML = escapeHTML(comment.body);

                commentSection.appendChild(commentBodyPElement);
            }
            commentSection.appendChild(document.createElement("p"));

            userComments.appendChild(commentSection);
        }
    }
};

Si enviamos este payload <h1>test</h1> tanto en el campo nombre como en el campo comentario vemos que no aparece la etiqueta de cierre </h1>

Si inspeccionamos ambos elementos vemos que tampoco se muestran

Si enviamos este payload <div><h1>test</h1></div> se interpretará tanto el html en el campo de nombre como en el de comentario

Si nos dirigimos a la página del post que hemos publicado https://0a8d00c003e3219481205c76008f00e5.web-security-academy.net/post?postId=2, si recargamos con F5 y capturamos la segunda petición que se hace a la web con Burpsuite podremos ver los comentarios en formato JSON

Si nos abrimos la consola del navegador podemos ver que solo se ejecuta la función escapeHTML() sobre los primeros <> y no sobre los demás

Para solucionar esto podemos hacerlo de esta forma para encodear toda la cadena y no solo la primera parte o encodear todo y almacenarlo de esa forma en la base de datos para no depender de que JavaScript solucione el problema de seguridad

Podemos usar este payload <><img src=1 onerror=alert(4)> para que lo encodee los primeros <> y luego que nos interprete todo el payload restante

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