diconium Blog

Chaining Bugs from Self XSS to Account Takeover

Written by Behnam Yazdanpanah | May 23, 2025 1:52:04 PM

 

Introduction

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.

 

Steps to reproduce the vulnerability: 

 

Attacker Steps:

The attacker edits the POC.html, replacing attacker.com with their own site and saves it.

Victim Steps:

  1. The victim logs into their account.
  2. The victim opens the attacker's site.

 

Impact and Remediation

 

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.

 

 

Key Takeaways

  • Always implement CSRF protection for sensitive actions.
  • Thorough input validation is crucial to prevent XSS and other injection attacks.
  • Security testing should consider chained vulnerabilities, not just individual flaws.