Introduction
For quite a long time, I’ve been hunting for vulnerabilities on the HackerOne platform, allocating a certain amount of time outside the main work to check out my new and favorite programs. Countless times, I stumbled upon a Cookie-Based XSS vulnerability, which will be the core content of this article. This type of vulnerability occurs when the value of the cookie parameter is reflected on the page. By default, they are considered self-XSS if we don’t prove the impact. This post will tell you how to exploit Cookie-Based XSS vulnerabilities with an example from testing applications belonging to a single company. In general, I received $7,300 for the Cookie-Based XSS research.
Ways to exploit Cookie-Based XSS
To execute javascript on the user’s side, we need to find a way to set a cookie and, if necessary, make a victim visit the page where the cookie is embedded. Here are possible ways to exploit Cookie-Based XSS:
CRLF injection
This vulnerability occurs when there is no proper detection and blocking of newline characters. We can inject a Set-cookie header in the response with any name and the value of the cookie and set it in the browser. Real-life example: Tricky CRLF injection on twitter.com domain in the redirect, — https://twitter.com/login?redirect_after_login=/jjjkkk嘊嘍Set-Cookie:jjjjj=a;domain=twitter.com
You can check disclosed reports of this type on HackerOne.
XSS vulnerability on a subdomain
It is necessary that XSS is publicly available and located on wildcard * .vulnerabledomain.com or the main domain. For many bug bounty programs, subdomains are out of scope, i.e., in most cases, bugs are either not accepted or are accepted but marked as “not eligible for the bounty.” To get a bug for the Cookie-Based XSS chain, you can invest your time searching for additional XSS on the subdomains (possibly, OOS) to prove that Cookie-Based XSS is exploitable and get a reward. When XSS is detected, we can set or remove cookies using the document.cookie
property.
Increasing Impact: Often, the victim, trusts the main domain vulnerabledomain.com
more than, for example, jira.vulnerabledomain.com
, especially with the URL /plugins/servlet/oauth/users/icon-uri?consumerUri=https://maliciousdomain.com
. It is more likely that the victim will go through the link leading to the source (main domain or subdomain) if the source is associated with a personal account or authorization. We can use an in-site redirect functionality for redirecting to the source like https://vulnerabledomain.com/login?redirectUrl=https://jira.vulnerabledomain.com/path
for the best outcome.
If the victim has an active session, the redirect will be automatic; otherwise, authorization is needed. If we insert the document.cookie
property on the page, the cookie will be set when the user clicks on a link. From the subdomain on which XSS is presented, a user can be redirected to the Cookie-Based XSS page, where the exploit will trigger, for example, capturing CSRF token value and sending the request for email address change. Thus, combining two XSS vulnerabilities can lead to account takeover if there are no associated problems, such as additional confirmation of changing an email with a password or entering a code from an old mailbox.
Usage of endpoints or test files to set cookies
We can detect endpoints or test files that allow setting cookies. It is enough to turn on the content discovery tool (dirb, dirserach, ffuf etc) along with parameters brute-forcing and start digging. If the developers forgot to make the cleanup, you can stumble upon such files or endpoints.
Recently I discovered a test servlet HTML page on which it was possible to set a cookie with an arbitrary name and value. Since it was a test page, there was no CSRF protection on the POST request, so if the victim had visited the page that will send a form automatically (or the POST method can be changed to GET), it would be possible to set an arbitrary cookie in the victim’s browser.
This bug was accepted as an alternative to CRLF injection, fixed by removing /examples/ directory, and a $150 bounty was paid for the low-severity bug.
Man in the middle attack
This method can be applied if there is no secure flag on the cookie. If you don’t recognize this flag or want to refresh your memory, I advise you to look at the “Cookie security” presentation from OWASP London 2017.
For a successful attack, the victim should be in the attacker’s network, or the DNS resolving can be influenced. To check the vulnerability, you should:
- Host
index.php
file with the following content:
<?php if ($_SERVER[‘HTTP_HOST’] == ‘non-existed-subdomain.vulnerabledomain.com’) { setrawcookie(“VID”,’\’+alert(123123123)+\’’, time()+36000, “/”, “.vulnerabledomain.com”,0,1); } ?>
- Add the following line to your
/etc/hosts/
file:127.0.0.1 non-existed-subdomain.vulnerabledomain.com
- Visit
non-existed-subdomain.vulnerabledomain.com
and then open the page on which the cookie is reflected.
A remarkable example of MITM exploitation on e.mail.ru is shown in this HackerOne report, which is publicly disclosed. As you can see, MITM was enough to prove a minimal impact, but the reward didn’t match the Stored XSS level, as only the “local” exploitation example was detailed. If the researcher had spent some time looking for XSS or CRLF injection (such bugs are countless on the company’s subdomains), it would be possible to increase the reward significantly.
But not all Bug Bounty programs accept Cookie-Based XSS via MITM. If scope exclusions say “Self XSS,” then this exploitation can be considered as such, and report status can be changed to informative or N/A, which is not always pleasant. Now I want to tell you about a similar case that happened to me during the regular hunt on the platform.
While testing the application, I suddenly noticed that the value of the Censored cookie is reflected in one of the subdirectories of the website. The first thing I checked is displaying the following characters ' "/<>
. It turned out that only <>
characters are filtered, and input is reflected inside the tag, which points out that we cannot go beyond <script></ script>
, but it also becomes clear that the rest of the characters are not filtered. Without overthinking, I embedded ‘-alert(document.domain)-’, and js was executed.
Since the developers did not set a secure flag on the cookie, the MITM method works in this case. I decided to send the report to the program with the following impact:
HackerOne staff member made it clear that this is considered a self-XSS, and I should try harder:
After the triager’s comment, I surfed the site to find CRLF injection or XSS to support the impact. I had to expand the list of the subdomains with the help of brute force with an extensive dictionary, scraping subdomains with SSL certificates, and using some other tricks. The result was not long, as I ran most of the tools in VPS. While looking for needed bugs, I found additional bugs, which I reported immediately, turning out-of-scope bugs to in-scope, if necessary. There were lots of Open Redirects and even one Improper Access Control bug for $5000, but I still couldn’t catch vulnerabilities needed for the chain. The improper Access Control case is quite exciting and dangerous; a whole subdomain was taken offline immediately due to the report. Perhaps in the future, I will reveal the report on my HackerOne page if the program becomes public.
A week later, I went through the results of the script for content discovery, where endpoint /verification
was found, for which I didn’t pay much importance at first. However, I still launched a script on that endpoint — the /verification/login
subdirectory was found. After I went through the URL, the /verification/login/?redirect_uri=https://vulnerabledomain.com
page popped up, which redirected to the redirect_uri
value after logging in or immediately redirected if there was a session though there was a protection. After some fuzzing in the burp intruder, open redirect protection bypass was discovered — vulnerabledomain.com@anotherdomain.com
. I tried to escalate the bug to XSS — payload javascript:alert(1) failed, javascript:alert(1)// too. But payload javascript://https://vulnerabledomain.com/%250A1alert(1):0
fired, because due to the presence of https://vulnerabledomain, the parameter passed the white list validation.
After excitedly driving the mouse around the alert window, I immediately switched to my Cookie-Based XSS. With the help of javascript:https://vulnerabledomain.com/%0A1?document%2ecookie%20%3d%20%27SID%3d137gf6g67f76fg6766123%5c%27-alert%28document%2edomain%29-%5c%27%3b%20expires%3dFri%2c%203%20Aug%202019%2020%3a47%3a11%20UTC%3b%20path%3d%2f%3b%20domain%3d%2evulnerabledomain%2ecom%3b%27%3a0
payload, a cookie was successfully set on *.vulnerabledomain.com
. After visiting the page with a freshly installed cookie, the desired alert window has appeared! Double XSS, yes! :) I updated a report from the “needs more info” state and was waiting for an answer.
On the same day, a “nice catch” arrived from the triager (if we can call it that), and the bounty was paid. God bless the companies that pay on the triage!
In addition, the bounty came to my account for the DOM-based XSS, which I used to install the cookie.
Testing results: $1000 + $1000 + $200 (OR)+ $100(OR) = $2300
This program has been operating for more than a year, but in less than a month, I took first place in it and got away with testing quite far. I tried fuzzing most endpoints, evaluating their reaction, understanding how the site works and testing the desktop application. This bug bounty has become one of my favorite programs on HackerOne. I hope you will find the same one too! :)
Also, precisely this program gave me a new boost, and with its help, I climbed to 2500 reputation (hello hoodie 👋) and got 36th place on the leaderboard for reputation in 90 days, which should give me fresh private invites. Though private invites seem to come regardless of my presence on the leaderboard, I often cancel old ones and wait for new ones in the queue.
Short brief
Cookie-Based XSS vulnerabilities are pretty exploitable. If you try and dig a little deeper, you can get a bounty instead of n/a, signal destruction, and -5 reputation points. If the program is old, it doesn’t mean that it doesn’t have vulnerabilities. If the fruitages hang on a tree for a long time, low-hanging fruits will be picked and immediately taken home (subdomain takeovers, XSS in the search field, etc.). Other fruits continue to hang, but higher. To get them, you need to make some effort. Sometimes it’s better to focus on one program for a long time, find as many vulnerabilities as possible, and constantly monitor it. It is better to find the program that you prefer and break it. Perseverance and the desire to understand how a web application and certain functionalities work and their interaction is the key to finding vulnerabilities in a bug bounty.