Hacking xAI for unreleased models and confidential documents

Introduction
This whole saga began in January 2025, when xAI's API console first launched and I started searching for vulnerabilities. Their app seemed very robust; there were no obvious lingering flaws, but something did catch my eye.
When I checked the response from the user profile endpoint, I noticed this:
{
"email": "[email protected]",
"emailVerified": true
}This doesn't seem very remarkable at first. However, it gave me the thought: If these variables are stored separately, could I change the email while setting emailVerified to true via a race condition?
Unlike most sign-up flows, xAI's let you change your email during the verification process, making this attack potentially possible.

The exploit
I imagined the attack would look something like this on the backend:
GET /verify-email?token={x} │ POST /change-email
─────────────────────────── │ ───────────────────────
user = getUser() │
check if x == user.token │
│ user.email = [email protected]
user.verified = true │ user.resetToken()
───────────────────────────────────────────────────────────
→ [email protected] is now verified !Essentially, when verifying my email while changing it at the same time, there could be a short window where the backend verified an email address using the token from the previous email. There was no guarantee that this would work though. Even if the backend was vulnerable, the timing required would still need to be extremely precise.
I was testing this exploit via a multi-threaded Python script, manually tuning the offset in each attempt. Eventually, I successfully hit the race condition. Through this attack, I was able to verify the email [email protected].
Now what?
This vulnerability had a couple of security implications. For one, if an email had a pending invite to a team, an attacker could create an account under it and gain unauthorized access. There was also a "Verified Domains" feature, allowing teams to verify their domain and automatically add anyone signed up under it to their team.
I promptly reported this to xAI's vulnerability reward program, and I was given a bounty of $2,500. Within a month, the vulnerability was completely patched.

The aftermath
This story doesn't end here though. For some reason, xAI never deleted the test accounts I created under the @x.com domain. When I checked one of the accounts in May of this year, I was surprised to see myself added to an "Internal xAI Team".

I immediately checked the models available to me and saw numerous internal ones, all of which I could access by creating an API key. I could also create my own internal teams, which would all be seeded with $10,000 in API credits.

Since Grok Business offered shared workspaces between teams, I began looking there next. The first thing that stuck out was the "Projects" feature.

Within these projects, users could create a template equipped with instructions and uploaded files.

I immediately found a few projects with extremely confidential information. For example, this "Security Agent" project had pentest findings, an SOC 2 report, and even an incident response plan.

Another project meant to help train "Security Specialists" included files on security operations for xAI's data centers.

I won't detail every sensitive file, but needless to say, this was already getting bad.
The final thing I checked was shared Grok chats. With Grok Business, if users in a team wanted to share a link to their chat, it would automatically become viewable across the whole team.

Sure enough, tons of chats were being shared with me, some being particularly sensitive (i.e model training, benchmarks, etc).
After seeing all of that, I submitted another report to their program and was rewarded an additional $7,000. They quickly disabled all of the accounts and implemented a fix that would prevent accounts authenticated via email from accessing internal teams.