During a recent bug bounty engagement, I discovered a fascinating chain of vulnerabilities that escalated from a seemingly benign self-XSS to a full account takeover.
This scenario highlights the importance of understanding how seemingly low-impact vulnerabilities can be leveraged in combination to create severe security risks. Let’s dive into the details
First XSS and WAF Bypass:
I started hunting on an online shop let's call it the redacted.com which was built with PHP and protected by Akamai WAF.
After some time working on the application, I found an endpoint that differed from the others.
Typically, they start with /xhr/mage/account , but this one was /xhr_preferences/index/change/ , which caught my attention.
This page was about customer preferences, where users answer a series of questions like their birth date or preferred brands to receive personalized product recommendations.
I simply input an HTML injection payload, and it worked.
Next, I tried the most famous XSS payload of all time: "><img src=1 onerror=alert(1)>
However, I received a 403 Forbidden response from the server: AkamaiGHost.
I modified the request slightly and attempted various XSS payloads for almost two hours to bypass the Akamai WAF.
Here are some resources you can try: WAF Bypass XSS Payloads
Unfortunately, none of them worked, and I was running out of ideas.
After taking an espresso and returning with a fresh mind, I reviewed my log history and was surprised to find that in the first name field, I was able to store an XSS payload.
However, attempting this elsewhere resulted in a 403 Forbidden response. What was different about that endpoint?
Notably, the request was sent in JSON format, and the response was 200 OK, but this parameter wasn't vulnerable to XSS.
I immediately changed my request to the /xhr_preferences/index/change/ endpoint from query-string to JSON.
It returned an HTTP/2 302 Found, indicating I was able to bypass the WAF, but it wasn't saved in my preferences.
So, what now? I needed to send a request like this:
and it got triggered
congratulations to me on my successful self XSS.
Second CSRF Bypass:
The backend used a simple mechanism: when users log into their accounts, it sets a cookie called csrf with a random value like Set-Cookie: csrf=7e3f4ebc6d40b5; Path=/.
Whenever I sent a POST request, it would check if csrf existed in both my cookie and header, and if they matched, it would return 200 OK.
If not, it would return:
As I mentioned before, the /xhr_preferences/index/change/ endpoint was different, so I deleted the CSRF header from my request, and it worked.
Now I had everything I wanted.
This vulnerability allows an attacker to run arbitrary JavaScript, leading to account takeover.
Attacker Steps:
The attacker edits the POC.html, replacing attacker.com with their own site and saves it.
Victim Steps:
This chain of vulnerabilities demonstrates how seemingly low-risk issues, when combined,Can lead to severe consequences.
Proper input validation and CSRF protection are essential to prevent such attacks.
Developers should always treat user-supplied input with caution and implement robust security measures.