Account Takeover via CSPT with Subsequent 2FA Bypass through Prototype Chain

Step 1. Client-Side Path Traversal
The CSPT primitive is fairly typical: the attacker changes the invite-sending request path PUT /api/v2/teams//invites/ to the email-change endpoint PUT /api/v2/user.
As a result, when the victim follows the invite link, the client sends a request that changes the victim's email address to one controlled by the attacker.
The interesting part is that CSPT only gives control over the URL, not the request body. According to HTTP semantics, PUT usually carries the new resource state in the request body. In this case, however, the server did not strictly require the new email to be supplied in the body and accepted it from a query parameter instead. This made account takeover possible.
Step 2. 2FA Bypass via Prototype Chain
Next, the researcher needed to log in to the compromised account and pass the OTP check. This was bypassed by sending proto instead of a valid code.
The issue relies on JavaScript property lookup. When reading an object property, JavaScript first checks the object's own keys; if the key is not found, it continues up the prototype chain to Object.prototype.
Based on the observed behavior, the server likely checked OTP validity with logic similar to:
if (pendingCodes[code]) { // issue session }
When the attacker sent: X-2FA-Code: proto there was no own key named proto in pendingCodes, but proto is an inherited property available on ordinary JavaScript objects. Therefore, pendingCodes["proto"] returned the object prototype — a truthy value. The condition passed, and the server issued a session.
💬 Discuss
Published
2026-05-05, 09:58