September 3, 2026
From a Forgotten Config Endpoint to Full OAuth2 Takeover (Critical)
وَالَّذِينَ جَاهَدُوا فِينَا لَنَهْدِيَنَّهُمْ سُبُلَنَا ۚ وَإِنَّ اللَّهَ لَمَعَ الْمُحْسِنِينَ﴾ سورة العنكبوت (٦٩)﴿
By Hossam Hussein
4 min read
Hello everyone 👋
I'm Hossam Hussein, a security researcher and bug bounty hunter — and I hunted this one shoulder-to-shoulder with my brother Mohamed Magdy (skymig0). This is the story of one of our favourite findings: a single unauthenticated configuration endpoint that handed us the application's OAuth2 client secret — which we then turned into a valid access token and used to reach a protected identity API holding real users and tenants.
One request. Full OAuth2 trust-boundary break. Critical.
Target Information
-
Program: Private (referred to here as
free-palestine.com) -
Type: ERP / B2B procurement & e-invoicing platform
-
Identity: the platform authenticates through a central SSO/identity provider ("Connect") and talks to a public identity API that manages users, tenants and roles.
ERP platforms are stitched together from many services — a frontend, a backend, message brokers, caches, search clusters, and an identity provider that ties accounts together. The more moving parts, the more places a secret can leak. That's exactly what happened here.
Summary
An internal configuration endpoint on free-palestine.com returned the application's entire property set in cleartext — with no valid authentication required. Among the exposed values were:
-
the Connect OAuth2
client_idandclient_secret -
an RSA private key
-
credentials for internal infrastructure (RabbitMQ, Redis, Solr, Elasticsearch)
-
internal IP addresses and service endpoints
Using the leaked OAuth2 credentials, I minted a valid access_token from Connect via the client_credentials grant, then used it to call the protected public identity API — reading users and tenants (PII), with scopes that also allow user/role modification.
This is the full chain: config leak → secret → token → unauthorized API access → PII + privilege manipulation.
Proof of Concept (PoC)
🧭 Part 1 — A config endpoint that shouldn't be public
While mapping the frontend's JavaScript, we noticed the app referenced an internal-looking properties/config route. We requested it directly — with no valid session:
GET /web/api/properties/ HTTP/1.1
Host: free-palestine.comGET /web/api/properties/ HTTP/1.1
Host: free-palestine.comIt returned 200 OK with Content-Type: application/json — a full configuration dump. No login required (not even a valid session/CSRF token).
💥 Part 2 — The secrets inside
This wasn't feature flags. It was the application's secrets:
| Exposed key | What it is |
| — -| — -|
| example.connect.client.id | OAuth2 client_id for Connect |
| example.connect.client.secret | OAuth2 client_secret |
| *.privateKey | RSA private key |
A leaked private key and OAuth2 client secret is already Critical on its own. But the best findings prove impact — so we kept going.
💥 Part 3 — Turning the secret into a token
OAuth2 has a grant designed for exactly this kind of credential: client_credentials. So we asked Connect for a token, using the leaked secret:
POST /connect/token HTTP/1.1
Host: connect.identity.example
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<LEAKED_ID>&client_secret=<LEAKED_SECRET>POST /connect/token HTTP/1.1
Host: connect.identity.example
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<LEAKED_ID>&client_secret=<LEAKED_SECRET>Connect happily replied:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "publicapi:user:read publicapi:user:update publicapi:tenant:* …"
}{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "publicapi:user:read publicapi:user:update publicapi:tenant:* …"
}A valid access token — no IP or environment restriction stopping a leaked secret from being used anywhere, and the granted scopes included user read/update and tenant management.
💥 Part 4 — Walking into the protected identity API
With the token, the protected public identity API was wide open:
GET /v1.0/users HTTP/1.1
Host: public-api.connect.identity.example
Authorization: Bearer <ACCESS_TOKEN>
{
"total_users": 34,
"users": [
{ "id": "00000000-0000–0000-0000-000000000000", "email": "user@example.com", "country_code": "NO" }
]
}GET /v1.0/users HTTP/1.1
Host: public-api.connect.identity.example
Authorization: Bearer <ACCESS_TOKEN>
{
"total_users": 34,
"users": [
{ "id": "00000000-0000–0000-0000-000000000000", "email": "user@example.com", "country_code": "NO" }
]
}Real users, emails, countries, IDs — and, per the token scopes, the ability to create/update users, manage tenants and assign roles.
Impact
-
Full OAuth2 trust-boundary break — leaked client credentials let an attacker authenticate to Connect as the application and mint valid tokens at will.
-
Unauthorized access to the identity API — enumerate and modify users and tenants, exposing PII (emails, names, country, IDs). A clear GDPR concern.
-
Privilege / role manipulation — the token scopes allow user creation/update and role assignment → escalation and persistence.
-
RSA private key + infrastructure credentials (RabbitMQ / Redis / Solr / Elasticsearch) and internal topology disclosed → broad secondary attack surface.
-
Scope crosses systems — a bug in the ERP app compromised a separate security authority (the identity platform). That cross-system reach is what makes it Critical.
Key Takeaways
-
Never serve your config to clients. Property/config endpoints belong server-side, behind auth — and should never contain raw secrets in the first place.
-
Read the JavaScript. The frontend told us this endpoint existed. Recon in the JS bundles is where so many "one request, Critical" bugs begin.
-
Prove the chain. "Secrets leaked" is good; "secrets leaked → valid token → authorized API calls → PII" is undeniable. Demonstrating impact (safely) is what turns a config leak into a Critical.
-
Secrets must be rotatable and scoped. Bind OAuth2 client secrets (IP/environment), grant least privilege, and assume any secret that touches a client is already burned.
Report Timeline
- Reported — unauthenticated config disclosure with the full OAuth2 → identity-API chain.
2.Triaged & Accepted as Critical; the team rotated the exposed secrets and fixed the endpoint quickly.
- Resolved — endpoint is no longer accessible; the disclosed secrets were rotated.
Researchers
This finding was a team effort — and more than that, it's the kind of work that turns colleagues into family.
Hossam Hussein
-
🐦 X/Twitter: https://x.com/h_hussein11
-
💼 LinkedIn: https://www.linkedin.com/in/hossam-hussein-393a05247/
-
🛡️ HackerOne: https://hackerone.com/hossam13?type=user
-
🎯 Intigriti: https://app.intigriti.com/profile/hossan_11
Mohamed Magdy (skymig0) — my partner on this report and my brother. I first met him through cybersecurity, but today he's family far beyond it — in this field and in life. This one is ours, brother. 🤝
-
🐦 X/Twitter: https://x.com/skymig0
-
🎯 Intigriti: https://app.intigriti.com/profile/skymig0
-
🛡️ HackerOne: https://hackerone.com/momya?type=user
-
💼 LinkedIn: https://www.linkedin.com/in/0xmagdy/
Thanks for reading. Read your target's JavaScript, follow the secret to where it leads, and prove the whole chain.
﴾وَالَّذِينَ جَاهَدُوا فِينَا لَنَهْدِيَنَّهُمْ سُبُلَنَا﴿