IDOR in Government Ownership API Exposed Private Business Owner PII via CR Number Enumeration
Severity: High
Bounty Awarded: $1,506
Program: Private Bug Bounty
Platform: Bugbounty.sa
Some IDORs are obvious immediately.
You change an ID.
Someone else’s data appears.
Easy.
Others look harmless at first because the application appears to return “public” information.
This bug started exactly like that.
At first glance, the endpoint looked like a normal commercial-registration lookup API tied to a government energy platform.
The request was simple:
GET /v2/public/profile/api/entity/owners/{crNumber}
The parameter looked predictable.
Commercial Registration numbers.
Nothing unusual.
But the response was not returning business metadata.
It was returning the people behind the business.
And that changed everything.
The Target
The affected platform belonged to a government-related service tied to regulated commercial activities and profile management workflows.
While browsing the application flow, I noticed several API calls tied to entity registration and ownership verification.
One endpoint stood out because it accepted a direct CR number inside the path:
/owners/{crNumber}
That immediately raises a common question during recon:
What happens if I replace the identifier?
Government systems frequently expose business lookup functionality, so the existence of a CR lookup itself was not suspicious.
The important question was:
What data is actually considered “public”?
Testing the Endpoint
Using an authenticated session, I intercepted a request similar to:
GET /v2/public/profile/api/entity/owners/40XXXXXXXX?api-version=2.0 HTTP/1.1
Host: api.redacted.gov.sa
Authorization: Bearer [redacted]
X-Requested-With: XMLHttpRequest
Origin: https://profile.redacted.gov.sa
Referer: https://profile.redacted.gov.sa/
The response returned ownership information tied to that commercial registration.
At first, it looked like standard registry data.
Then I looked closer.
The API response included:
full owner name
nationality
birth date
national ID
ownership relation
additional ownership metadata
Example response:
{
"crNumber": "40XXXXXXXX",
"ownersList": [
{
"ownerName": "Ahmed A. Al-Redacted",
"ownerNationalityEN": "Saudi Arabia",
"ownerBirthDate": "199X-XX-XX",
"ownerId": "1101XXXXXX",
"relationType": "Owner",
"isAllowed": true
}
]
}
That was no longer business information.
That was personally identifiable information tied to real individuals.
At this point, the next step was simple:
change the CR number.
And the system happily returned ownership information for entirely different entities.
No authorization validation.
No ownership checks.
No restriction ensuring the authenticated user was associated with the requested business.
Just:
supply CR number
↓
receive owner PII
Why This Was More Serious Than “Public Business Data”
This is where the report initially became contentious.
The first triage response closed the issue as Not Applicable with the reasoning that commercial registration data was already publicly accessible through another government portal.
Which, technically, was partially true.
Basic entity information was public.
Things like:
business name
registration status
entity details
commercial metadata
But that was not what this endpoint exposed.
The vulnerable API disclosed private owner-level information that was not available through the public registry lookup.
That distinction mattered.
A lot.
Because there is a major difference between:
"this company exists"
and
"here is the owner's personal information tied to that company"
The exposed data included sensitive attributes tied directly to individuals, not just organizations.
That changes the privacy impact entirely.
The Triage Dispute
This became one of those classic bounty moments where the technical vulnerability was correct, but the impact interpretation initially diverged.
The report was first closed as:
Status: Not Applicable
The reasoning:
"all information are public"
Instead of arguing emotionally, I focused on demonstrating the difference between:
public commercial entity data
private ownership PII
I compared the official public registry output against the vulnerable API response and showed that the public portal only exposed organizational details, while the API disclosed sensitive owner information that was not otherwise publicly accessible.
That clarification changed the outcome.
Shortly afterward:
Status: Approved
Later:
Status: Confirmed
Eventually:
Status: Resolved
One thing I’ve learned from government and enterprise programs:
Sometimes the hardest part is not finding the vulnerability.
It is explaining why the data actually matters.
Why Enumeration Matters
Commercial Registration numbers are not secret.
They are structured identifiers.
Predictable.
Often sequential or easily discoverable through public documents, invoices, company websites, procurement records, or open registries.
That means the attack surface was realistically enumerable.
An attacker could automate requests across large CR ranges and harvest ownership information at scale.
At that point, the issue stops being:
single-record exposure
and starts becoming:
bulk PII collection infrastructure
That dramatically changes risk.
Especially for:
government-linked systems
regulated industries
business ownership databases
identity-linked records
Even if certain fields appear individually “low sensitivity,” aggregation changes the impact.
At scale, the data becomes highly valuable for:
social engineering
target profiling
identity correlation
fraud campaigns
business-owner targeting
The Real Lesson
This bug reinforced something important:
“Public” is often misunderstood during triage.
Just because a system contains some public data does not mean every connected dataset becomes public too.
Applications frequently mix:
public entity data
private account data
internal ownership records
identity-linked metadata
And once those boundaries blur, IDORs become extremely dangerous.
Especially in government ecosystems where APIs are built around trusted internal assumptions.
Hunter Takeaways
1. Government APIs Frequently Trust Identifiers Too Much
Many internal systems assume identifiers like:
CR numbers
national IDs
license IDs
permit numbers
are only used by legitimate workflows.
Always test authorization boundaries around them.
2. Public Data ≠ Public PII
This matters constantly in triage.
Ask:
Is the exposed data already publicly accessible in the same form?
If not, explain the distinction clearly.
3. Aggregation Changes Severity
One leaked record may look minor.
Bulk enumerable access changes everything.
Always think about scale.
4. Triage Communication Matters
The vulnerability itself did not change between:
Not Applicable
and
Approved
The explanation did.
Good technical clarification can completely change report outcomes.
Final Thoughts
The actual vulnerability was simple:
replace CR number
↓
receive someone else's ownership data
No complex bypasses.
No race conditions.
No clever payloads.
Just missing authorization on a sensitive government API.
But the interesting part was not the bug itself.
It was the classification battle around what counts as “public.”
Because in many systems, the dangerous exposures are not obvious leaks.
They are trusted internal datasets accidentally exposed behind familiar-looking identifiers.

