It was March and Intigriti published a new XSS challenge. Since good XSS challenges are always a way to learn new interesting methods, I gave it a try.
The challenge website (https://challenge-0321.intigriti.io/) contains the general rules and an input field to enter notes.
If we enter and store a note, the browser sends a POST request to the server, containing the note, a CSRF token, and the user’s PHP session id.
POST / HTTP/1.1
Host: challenge-0321.intigriti.io
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Cookie: PHPSESSID=<session id>
[...]csrf=<csrf token>¬es=this+is+an+example
As a result, the server would respond with the website content including the new note, a new CSRF token, and a timestamp when the website was created.
<html>
[...]
<div class="card-container">
<form method="POST" action="./" id="update-notes">
<div class="card-header">Your notes:<span id="actions"><a id="notes-save" href="#">save</a></span></div>
<p id="notes-display" class="card-content" contenteditable="true">this is an example</p>
<input type="hidden" name="csrf" value="e79f7691593b3775c173f13512979d00"/>
<input type="hidden" id="notes-value" name="notes" value=""/>
</form>
</div>
[...]
<!-- page generated at 2021-03-25 10:34:19 -->
</html>
On the server-side, the backend checks the submitted note. If we send a special character to escape the HTML element, such as <
or >
, the server would sanitize them to prevent injection attacks. Furthermore, if an URL or an email address is contained in the note, the server would convert them to <a>
elements containing the URL or mail as href.
For instance, the note “http://a.b or [email protected]” would be converted to:
<a href="http://a.b" target="_blank">http://a.b</a> or <a href="mailto:[email protected]">[email protected]</a>
Playing around with email addresses and reading in the RFC 5322, we know that the first part of the email address, the part before the @, can be wrapped in double-quotes. In this case, the server does not sanitize the double-quotes around the email address and we can escape the HTML element.
As before, adding double-quotes to the previous example “a”@b.com leads to <a href="mailto:"a"@b.com">"a"@b.com</a>
with the letter a
being outside of any attribute.
With this in mind, it is easy to add any attribute to the <a>
element and abuse it to trigger XSS. An example would be the mouseover attribute:
"onmousemove=alert('flag{THIS_IS_THE_FLAG}');"@evil.com
is converted to
<a href="mailto:" onmousemove="alert('flag{THIS_IS_THE_FLAG}');"@evil.com"">"onmousemove=alert('flag{THIS_IS_THE_FLAG}');"@evil.com</a>
In order to deliver the XSS to any user, we still have to bypass the CSRF token.
Looking at an example CSRF token e79f7691593b3775c173f13512979d00, we can guess from the length and used characters that it might be an MD5 hash. At the bottom of the webpage, we can also see a timestamp when the website was created on the server-side. As it is a typical mistake to take the current UNIX timestamp, hash it and use it as a CSRF token, we should try it out.
For a quick check, we can use CyberChef together with the timestamp and CSRF token from the previous example.
As we can see, the output hash matches the aforementioned CSRF token.
Knowing this, we can now create a PoC webpage which the victim would open. The website first sends an initial request to the server to generate a new CSRF token. Due to CORS, the webpage cannot simply read the newly generated CSRF token from the response, but it can guess it, as we approximately know the created timestamp. Then the website sends the second request with the XSS payload.
To reproduce the vulnerability, the following HTML page can be used in Firefox.
- Open https://challenge-0321.intigriti.io/ with Firefox to create a session ID
- Open poc.html with Firefox
- Click Send Payload a few times, as sometimes the timestamp is incorrect
- Go back to https://challenge-0321.intigriti.io/ and hover the notes field
poc.html
As always, this was an interesting challenge. Moreover, it was realistic as this XSS vulnerability was similarly discovered in Microsoft Outlook.
My solution is only possible with user interaction, as the user is required to hover the notes field. Unfortunately, I did not find a solution without user interaction, and looking forward to other writeups.
A little follow-up:
I was selected as one of the top writeup authors and won a €50 swag voucher. Thank you Intigriti for the great challenge and the swag!