YesWeHack reward card — Critical 9.1, Information Disclosure CWE-200
YesWeHack reward card — Critical 9.1

Summary

Class
CWE-200 — Information Disclosure
CVSS
9.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)
Endpoint
POST /api/v1/organismes/get-by-uai-siret
Vector
Unauthenticated endpoint returning full MongoDB document with no field projection
Impact
Secret API keys disclosed — write access to data submission endpoints
Status
Fixed

Discovery

The target was a government application that manages data submissions from registered organisations. Each organisation is identified by two public national reference numbers — a SIRET (business registration) and a UAI (facility code). These identifiers are published in national registries and are not secrets.

While mapping the API surface, I found a POST endpoint at /api/v1/organismes/get-by-uai-siret designed for looking up organisations by these two public identifiers. The endpoint required no authentication — no session token, no API key, nothing.

POST /api/v1/organismes/get-by-uai-siret HTTP/2
Host: [REDACTED].gouv.fr
Content-Type: application/json

{
  "uai": "[REDACTED]",
  "siret": "[REDACTED]"
}

The server returned a 200 OK with the full MongoDB document for the matching organisation — no field filtering, no projection. Buried in the response alongside public information was a field called api_key:

{
  "uai": "[REDACTED]",
  "siret": "[REDACTED]",
  "nom": "[REDACTED]",
  "api_key": "mfk_[REDACTED]",
  "api_siret": "[REDACTED]",
  "api_uai": "[REDACTED]",
  "mode_de_transmission": "API",
  "api_version": "v3",
  ...
}

Exploitation

The disclosed api_key is the Authorization: Bearer credential for the data submission API at /api/v3/dossiers-apprenants. I validated the key non-destructively by hitting the test route:

POST /api/v3/dossiers-apprenants/test HTTP/2
Host: [REDACTED].gouv.fr
Authorization: Bearer mfk_[REDACTED]

→ 200 OK (key accepted)

The full attack chain:

  1. Obtain any organisation's SIRET and UAI from the public national registry
  2. Send an unauthenticated POST to /api/v1/organismes/get-by-uai-siret
  3. Extract the api_key from the response
  4. Use it as Authorization: Bearer to submit or overwrite data on /api/v3/dossiers-apprenants

No authentication required at any step. The only input is two publicly available numbers.

Impact

  • Credential disclosure — secret API keys for every API-transmitting organisation exposed to unauthenticated callers
  • Data integrity — the disclosed key grants write access to the data submission endpoint, allowing an attacker to submit fabricated records or overwrite legitimate submissions on behalf of any organisation
  • Scale — every organisation registered on the platform with mode_de_transmission: "API" was affected
  • No authentication barrier — the entire chain from credential theft to data submission requires zero authentication

Remediation

The team fixed the issue within days:

  • Applied a minimal projection on the public lookup endpoint — api_key, api_siret, api_uai, and all mode_de_transmission fields are now stripped from the response
  • Rotated all API keys that had been exposed through the endpoint
  • Stopped serving production credentials from staging and pre-production environments (which shared the same data)

Takeaways

When an API returns a raw database document, assume it leaks too much. MongoDB's schemaless nature makes this especially dangerous — fields get added to documents over time, and unless the API explicitly projects only the fields it intends to expose, new sensitive fields silently leak the moment they are stored. Always test unauthenticated endpoints with valid input. The most dangerous information disclosures are the ones that return 200 OK with more data than anyone expected.