October 19, 2026
How Email-Change Token Exposure Led to Access to To Private Repositories
Imagine if Server Had to Hand You the Keys to Someone Elseβs Inbox

By Aceproulx
3 min read
- 1 Imagine if Server Had to Hand You the Keys to Someone Else's Inbox
- 2 Recon: Watching the App Talk to Itself π§°
- 3 The Discovery: The Confirmation Token Was Justβ¦ Sitting in the Response πͺ
- 4 Confirmed. No Inbox Required. π
- 5 Why This Actually Matters: It's Not Just "I Can Lie About My Email" π’
Imagine if Server Had to Hand You the Keys to Someone Else's Inbox
Let me start with this:
I asked to change my email. The server replied with everything it needed to approve it β minus actually needing me to check my inbox. Turns out confirming an email change doesn't require your email, if the app tells you the answer up front. Verification is supposed to be a secret handshake. This one was printed on the invitation.
This is the story of how a single leaked token on an account settings page could let anyone claim a company's email domain, peek at private organizations tied to it, and β in the worst case β walk straight into private repositories. No phishing, no social engineering. Just reading a JSON response a little too closely.
Recon: Watching the App Talk to Itself π§°
REDACTED is a dev-tooling platform where, like most SaaS products, your email address doubles as your identity β it's how organizations recognize "oh, this person belongs to our domain, let them in."
So naturally, changing your email is a sensitive operation. It should require proving you actually control the new inbox. Click "Change email," get a confirmation link, click the link, done. Standard stuff.
I went to the account page, kicked off an email change, and β out of habit more than suspicion β watched the Network tab.
The Discovery: The Confirmation Token Was Justβ¦ Sitting in the Response πͺ
The request hit:
/api/user/api/userAnd the JSON that came back included:
{
"newEmail": "<requested_email>",
"hash": "<verification_token>"
}{
"newEmail": "<requested_email>",
"hash": "<verification_token>"
}That hash field is not cosmetic. It's the exact value later used to confirm the change:
https://api.<redacted>.com/user/confirm-change-email?email=<token>https://api.<redacted>.com/user/confirm-change-email?email=<token>In other words: the server generated a secret meant to prove "yes, this person received the confirmation email" β and then handed that same secret straight back to the browser, before any email was ever opened.
So I copied it. Pasted it into the confirmation URL. Hit enter.
Confirmed. No Inbox Required. π
The email change went through immediately β no clicking a link in an actual inbox, no waiting, no second factor. Just the token the app had already shown me.
Which means the only thing stopping someone from "verifying" an email they don't own was⦠nothing.
Why This Actually Matters: It's Not Just "I Can Lie About My Email" π’
On its own, confirming an unowned email sounds like a party trick. The real impact shows up once you realize REDACTED uses email domains to tie people to organizations. So:
1. Private organization enumeration. Change your email domain to match a target company's domain, and the app starts showing you:
- Names of private organizations tied to that domain
- How many members each one has
- Whether they're on a paid plan
None of that is supposed to be public. It's quiet business intelligence about a company's internal tooling footprint, handed over because the domain field says so.
2. A path toward private codebase access. If an org admin ever approves a join/access request from an account sitting on "their" domain β not an unreasonable thing for a busy admin to rubber-stamp β the attacker is now inside private repositories. Source code, inline secrets, internal docs, issue trackers, the works. From there it's a short hop to IP theft, supply-chain risk, or credential leakage.
3. Possible license/subscription abuse. If the org has a paid seat available and the join request gets approved, that's a seat consumed that shouldn't have been β a financial impact layered on top of the access one.
None of this requires sophistication. It requires reading a response body.
Why This Happened
Classic sensitive-token-in-client-response mistake:
- The server generated a confirmation token meant to gate an email change.
- Instead of only ever sending that token through the email channel it was supposed to verify, it echoed the token back in the API response that triggered the change.
- The confirmation endpoint then trusted whatever token showed up β with no way to distinguish "token I emailed to the real owner" from "token the client already had."
The entire point of an email-verification token is that possessing it proves you read an email. Handing it back in the same response that requests the change quietly erases that guarantee.
Impact
- Anyone can "verify" ownership of an email they don't control, with zero access to the inbox.
- Enables domain impersonation β attacker-controlled accounts can masquerade as belonging to a target company's domain.
- Exposes private organization names, headcounts, and subscription status to anyone willing to try.
- Opens a realistic (if admin-dependent) path to private repository access and license abuse.
Reported severity: P2 β Severe.
Fix Recommendations
- Never include the email-confirmation token in any API response the client can read. It should only ever leave the server inside the actual email sent to the new address.
- Treat the confirmation endpoint as a proof-of-inbox-access check, not a "did the client send back what we gave them" check.
- Add rate limiting and anomaly detection around email-change attempts, especially ones using domains tied to existing organizations.
- Require admin approval workflows to independently verify domain ownership before granting org-level visibility or access, rather than trusting a freshly confirmed email alone.
Takeaway
Verification tokens only verify something if the party being verified is the only one who ever sees them. The moment a "prove you got this email" secret shows up anywhere before the email does, the whole mechanism becomes theater β technically present, functionally decorative.
This one, by the way, turned out to already be known internally by the time it got triaged β filed as a duplicate of an earlier report. Which is its own small lesson: if you can find it by just watching your own Network tab, assume someone already has. π