It was supposed to be the boring part of recon.
I had a new target. A multi-tenant SaaS in the construction-tech space, decent attack surface, no obvious low-hanging stuff in the burp history. First thing I always do is just *sign up*. Make an account, click around, see what the app looks like from the inside. I almost never find anything in the first twenty minutes.
## This time was different. The throwaway
I used the email I always use for first-look accounts. A fresh `@yopmail.com` address. If you've never used Yopmail, it's one of those public mailboxes where anyone who guesses your address can read your inbox. Hunters love it because you don't have to register. Pick a name, it exists.
I picked `XXXX@yopmail.com`. Set a password. Typed in a fake job title. Hit submit. Clicked the verification link through Yopmail's web inbox. Signed in.

Then I looked at the login response.
```json
{
"_id": "USR269123",
"email": "XXXX@yopmail.com",
"companyId": "152734",
"company": {
"_id": "152734",
"name": "test",
"domain": "yopmail.com",
"createdAt": "2026–01–12T05:02:32.733Z",
"users": [
{ "user": "033569", "role": "admin", "assignedOn": "2026–01–12T05:02:32.731Z" }
]
}
}
I read it twice. — `createdAt: 2026–01–12`. Four months before me. I signed up *today*. I did not create a company. There was no "create company" step in the signup wizard. So whose company is this?
## The first weird thing
There was actually a second weird thing I noticed before I even queried anything else. On my brand-new account, two fields were already filled in.
A phone number. `+91 830085XXXX`, tagged with country code `US`. Wat.
An avatar. `Screenshot+2023–03–02+at+4.18.59+PM.png`, sitting on an S3 bucket somewhere, clearly uploaded by someone else three years ago.
I never uploaded a screenshot. I'm not the kind of person who uploads a selfie during signup. And I definitely didn't type in someone else's phone number.
The signup pipeline was copying data from somewhere.
## Pulling the thread
If I'm in a tenant I didn't create, the tenant has other members. So I asked it.
curl -s 'https://api.vulnerable.com/api/v4/enterprise/users' \
-H "Authorization: Bearer $TOKEN" \
-H "x-company-id: 152734"
15 users' information came back.
All of them on `@yopmail.com`. All of them with email, full name, phone number (most `+91`, some `+1`), job title and last-active timestamps. One of them had a full project record. Name, street address, postal code, city, state, and geolocation coordinates (lat/lng).
These were other people's testing accounts. Some looked like internal QA. Some looked like other bug hunters poking at the same target. One had a stored XSS payload sitting in their first name (`<u>Test</u>`), which was a whole different finding waiting to happen.
I had signed up with a throwaway email, clicked one verification link, and read the contact details of fifteen other people. Zero exploitation. Zero auth bypass. Zero clever payload. Just sign up.
## Why this works
The app has a `company.domain` field on every tenant. When someone signs up, the backend looks up "is there an existing tenant whose `domain` matches the sign-up email's domain?" and if yes, it silently joins the new user into that tenant. No invite. No admin approval. No proof that you actually own that domain.
Someone, at some point, created a "test" company and put `domain: "yopmail.com"` on it. Probably a developer who didn't think anyone would notice. From that moment on, every yopmail signup got auto-merged in.
That's the visible failure.
## Why it's worse than it looks
The yopmail case is the one I could trip into safely. The tenant I joined was full of other testers, so the leak was visible-to-tester, quickly noticeable, and not a privacy disaster for actual customers.
The interesting failure is everything symmetric to it.
## The report
Steps to reproduce were the boring part because there's barely anything to do.
I wrote it up at High. CVSS 7.5. OWASP framing is A01 Broken Access Control and A04 Insecure Design, but honestly, calling this "access control" undersells it. The access control is technically working. The problem is the system *decided* I was a member of someone else's group based on the string after my `@`.
- Register
curl -s -X POST 'https://api.vulnerable.com/api/v1/users/register' \
-H 'Content-Type: application/json' \
-d '{
"email":"XXXX@yopmail.com",
"password":"Your Strong Password",
"firstName":"time",
"lastName":"test",
"jobTitle":"QA"
}'2. Click the verification link in the @yopmail inbox.
3. Sign in. The companyId in the response is not yours.
TOKEN=$(curl -s -X POST 'https://api.vulnerable.com/api/v4/users/signin' \
-H 'Content-Type: application/json' \
-d '{"email":"XXXX@yopmail.com","password":"Your Strong Password"}' \
| jq -r '.result.token')4. Read the team list of the tenant you got merged into.
curl -s 'https://api.vulnerable.com/api/v4/enterprise/users' \
-H "Authorization: Bearer $TOKEN" \
-H 'x-company-id: 152734'
Result: You guys will get the all PII information from the tenet.5. Result: You guys will get all PII information from the tenant.
## What I took from it
- Read the signup response: I almost closed the tab. I almost moved on to the "real" testing. Burp, fuzz endpoints, look for IDORs in deep flows. The whole bug was in the JSON I got back from `POST /signin ', sitting there in plain text, four months older than my account.
- Boring isn't boring: Multi-tenant isolation is the kind of thing every SaaS swears they got right, and then half of them get it wrong in a way that's invisible until somebody signs up with the right email. yopmail is a great canary. If a public mailbox domain silently joins you into a tenant, something is wrong with the tenant model.
- The yopmail tenant just happened to be where I could see it.
console.log("Thank You");