Worked example · 2025-02
Cardex session-signer-key frontend leak — Abstract Chain — 2025-02-18
Summary
Cardex was a blockchain trading-card game on Abstract Chain, an Ethereum L2 built on the ZK Stack with native account-abstraction primitives (smart-account wallets as the default user-account model, session-key delegation as a first-class UX surface). Cardex's gameplay flow asked players to sign a "session" transaction at the start of play; the session granted Cardex's session-signer key authority to execute gameplay-relevant operations (buyShares, sellShares, share-transfer between Cardex contracts) on the player's smart-account wallet without per-action signing prompts, for the duration of the session. The session-key UX is the central T13.003-relevant primitive — the user authorises an ephemeral, scope-bounded delegated signer in exchange for friction-free gameplay.
Cardex's session-key implementation contained two compounding architectural defects:
- Shared session signer. Rather than minting a per-user session signer at session start (the cleanest implementation pattern, which would limit blast radius from a single signer compromise to a single user), Cardex used a single shared session-signer key for all users.
- Frontend-inlined key material. The shared session signer's private key was inlined into Cardex's frontend JavaScript bundle. The key was not stored on a backend signing service that the frontend invoked over an authenticated channel — it was distributed to every user's browser as part of the dApp's static client code. Anyone inspecting the dApp's frontend source could extract the key.
On 2025-02-18 starting around 06:07 EST (11:07 UTC), an attacker who had extracted the leaked session-signer key from the frontend bundle began executing session-key-signed transactions against Cardex player accounts whose sessions were currently active. The attack pattern: the attacker identified an open session belonging to a victim, initiated a buyShares transaction on the victim's behalf (using the compromised session signer), then transferred the resulting shares to attacker-controlled addresses, and finally sold the transferred shares on the Cardex bonding curve to extract ETH. Over the ~7-hour attack window, 180 ETH ($400K-$484K depending on price snapshot) was extracted from approximately 9,000 affected accounts.
Within ~30 minutes of attack onset, Abstract Chain core team contributors and security researchers identified Cardex as the source of the exploit and began coordinating containment. Within hours, the Cardex team blocked application access, deployed a session-revocation site allowing affected users to revoke the compromised session signer's authority on their smart accounts, and upgraded the Cardex contract to prevent further session-signed transactions from the compromised key. Cardex subsequently shipped a post-incident patch separating per-user session signers and removing the frontend-inlined key.
For OAK's purposes the case is the canonical 2025 worked example for OAK-T13.003 sub-pattern (a) — dApp-side key compromise. The smart-account-side scope-enforcement on the affected accounts was working correctly: ERC-20 and NFT operations on the affected accounts were structurally outside the session's registered scope and were not extracted; only the in-scope operations (buyShares, sellShares, share-transfer between Cardex contracts) were exploitable. The failure was entirely at the dApp's session-signer-key storage layer. The case is the cleanest 2025 anchor for the T13.003 sub-pattern (a) sub-class and is structurally distinct from the Loopring 2024 case at examples/2024-06-loopring.md (which sat in T11.002 broadly construed and the smart-wallet-recovery-flow class within T13's account-abstraction scope, as a sub-class outside T13.003 proper).
Timeline (UTC)
| When | Event | OAK ref |
|---|---|---|
| Pre-event | Cardex deployed on Abstract Chain with session-key gameplay UX; shared session signer for all users; signer's private key inlined into the dApp's frontend JavaScript bundle | T13.003 surface (latent — sub-pattern (a) dApp-side key compromise + sub-pattern (d) within-application reuse) |
| Pre-event | Abstract Chain native smart-account session-key scope enforcement implemented per spec; correctly limits session-signer authority to registered selectors (buyShares, sellShares, share-transfer) |
(smart-account-side correctness baseline — not the failure surface) |
| 2025-02-18 ~11:07 UTC (~06:07 EST) | Attacker who has extracted the leaked session-signer key from the frontend bundle begins executing session-key-signed transactions against session-active Cardex player accounts; first suspicious transaction publicly flagged by a developer | T13.003 extraction onset (sub-pattern (a)) |
| 2025-02-18 ~within 30 min | Abstract Chain core contributors and community security researchers identify Cardex as the source of the exploit; cross-checking the dApp's frontend source reveals the inlined session-signer key | (defender response — community detection within minutes) |
| 2025-02-18 ~within hours | Cardex team blocks application access; deploys session-revocation site allowing affected users to revoke the compromised signer's authority on their smart accounts; upgrades Cardex contract to prevent further session-signed transactions from the compromised key | (operator response — multi-layer containment) |
| 2025-02-18 ~end of day | Total extraction reaches |
(extraction window closure) |
| 2025-02-19 onward | Cardex publishes post-incident statement; ships patch separating per-user session signers and removing frontend-inlined key material; The Block, Decrypt, CryptoRank, QuillAudits publish forensic write-ups | (transparency / cohort signal) |
| Days following | Attacker laundering routes proceeds through standard EVM mixing rails | T7.001 (off this example's framing) |
| Continuing | No public on-chain recovery; no civil-forfeiture action; no named-individual attribution | (recovery state) |
What defenders observed
- The smart-account-side session-key scope enforcement worked correctly; the failure was entirely at the dApp layer. Affected users' ERC-20 token balances and NFT positions on the same smart accounts were not extracted because those operations were structurally outside the session's registered scope — the smart-account contract correctly rejected any session-signed transaction whose target/selector was not in the registered allowlist. This is the strongest piece of evidence in the 2024–2025 OAK corpus that session-key scope-tightness materially limits blast radius even when the signer key is fully compromised. Defender practice for session-key-consuming applications should treat scope-tightness as a first-class control independently of signer-key-storage hygiene, because the controls fail independently and the orthogonal protection is operationally meaningful.
- The dApp-side session-signer key was inlined into the client-distributed frontend bundle, which is a structural error class, not a configuration mistake. A session signer's private key in the frontend bundle is structurally indistinguishable from a backend secret being committed to a public Git repository — except that the frontend bundle is intentionally distributed to every user's browser, so the key is not merely accidentally exposed, it is actively distributed. Defender practice for session-key-consuming dApps should treat any session-signer key storage that is reachable from client-side code as a high-severity finding requiring re-architecture, not as a configuration item to be tightened. The cleanest implementation pattern is per-user session signers minted on a backend signing service that the frontend invokes over an authenticated channel — this limits blast radius from a single signer compromise to a single user and removes the architectural surface that produced the Cardex incident.
- The "shared signer" architecture multiplied the blast radius beyond what a per-user signer would have allowed. Even if the frontend-inlined-key error had been avoided, a backend-stored shared signer represents a single point of compromise for all session-active users. The Cardex incident's 9,000-affected-account scale would have been reduced to whatever the largest single-user-balance was, had per-user signers been used. Defender practice for session-key UX should default to per-user signer issuance with an explicit operator-side justification required to use a shared signer, not the inverse default.
- Bounded session-key scope is an operationally-meaningful defence-in-depth layer that protected ERC-20 and NFT positions in this incident. Cardex's session scope was specifically
buyShares/sellShares/ share-transfer-between-Cardex-contracts; the smart-account contract correctly rejected session-signed attempts to transfer ERC-20 tokens or move NFTs out of the affected accounts. This is the operational dividend of session-key scope-tightness: when the signer-key compromise is total, the scope-tightness still limits realised loss to the in-scope-asset-class. The defender lesson generalises: session-key scope reviews at delegation time should evaluate "what is the worst case if the signer key is fully compromised" and should treat that worst-case bound as a first-class user-communication item. - Detection-to-containment was unusually fast — minutes to identify, hours to contain. The Abstract Chain core team / Cardex team / community security researcher coordination produced an unusually fast response timeline: ~30 minutes from first suspicious transaction to identification of Cardex as the source; ~few hours from identification to multi-layer containment (application access blocked, session-revocation site deployed, Cardex contract upgraded). This compares favourably with the typical session-key-related incident response timeline. The structural enabler was that Abstract Chain's smart-account model surfaces session-key registrations on-chain as discrete, observable events, and the ZK Stack's contract-upgrade primitives allowed Cardex to deploy the patch quickly. Defender practice for session-key-consuming dApp ecosystems should pre-position session-revocation infrastructure as a first-class incident-response asset.
- Pseudonymous attribution is the realistic outcome for this class. The compromised key was discoverable to anyone inspecting the frontend bundle; the realised attack was opportunistic; no public forensic provider has linked the extraction transaction set to a specific operator cluster. Funds laundered through standard EVM mixing rails. Contributors should not over-claim attribution simply because the on-chain transaction set is detailed.
What this example tells contributors writing future Technique pages
- OAK-T13.003 sub-pattern (a) (dApp-side key compromise) has a canonical 2025 worked example anchor in Cardex. The Cardex case is the cleanest 2025 worked example for the dApp-side session-signer-key compromise sub-class. Future T13.003 worked examples that exercise sub-pattern (a) should cross-reference this case for the canonical architectural-defect-class taxonomy: shared signer (vs per-user signer issuance), frontend-distributed key material (vs backend signing service with authenticated client channel), and the failure-class distinction between dApp-side session-key-storage hygiene and smart-account-side scope-enforcement.
- Smart-account-side scope-enforcement and dApp-side session-key-storage are independent failure surfaces. The Cardex case demonstrates this independence cleanly: scope-enforcement worked (ERC-20 / NFT positions protected), key storage failed (the in-scope assets were drained). Contributors writing T13.003 detection-signal entries should preserve the dimension explicitly: scope-enforcement-side defects (T13.003 sub-pattern (b) — over-broad scope at delegation; sub-pattern (c) — revocation / expiry failure) and storage-side defects (sub-pattern (a) — dApp-side key compromise; sub-pattern (d) — cross-application reuse) require structurally different defender controls. Defender practice for the entire T13.003 surface should treat the four sub-patterns as independent control surfaces and should not assume that fixing one closes another.
- Per-user-signer issuance is the cleanest implementation pattern; shared signers are an architectural anti-pattern even with proper key storage. The Cardex case illustrates the multiplicative effect of the shared-signer architecture: 9,000 affected accounts in a single incident, vs the typical per-user-signer architecture's bound at 1 affected account per signer compromise. Contributors writing the OAK Mitigations layer should encode "default to per-user session signer issuance" as a first-class architectural recommendation for session-key-consuming dApps, and should treat shared-signer architectures as requiring explicit operator-side justification.
- Session-key scope-tightness materially protected user assets in this incident; the protection generalises. Cardex's session scope was tight (buyShares / sellShares / share-transfer-between-Cardex-contracts only); ERC-20 and NFT positions were structurally protected even under total signer compromise. Contributors writing future T13.003 worked examples should evaluate scope-tightness as a defence-in-depth control independently of signer-key-storage hygiene, and should explicitly compute the realised-loss bound under scope-tightness assumptions ("what is the worst case if the signer key is fully compromised") as a first-class user-communication item.
- The Abstract-Chain-native smart-account model is one of the cleanest 2025 worked examples of session-key UX as a chain-level primitive. Most ERC-4337 smart-account session-key implementations sit at the application layer (session-key module installed per-user on a smart account); Abstract Chain's session-key UX is closer to native L2-level integration. The Cardex incident demonstrates that the chain-level integration does not in itself reduce the dApp-side architectural-defect surface — the load-bearing controls still sit at the dApp's signer-storage architecture. Contributors writing future T13.003 worked examples on chain-native-session-key-UX deployments (Abstract, Starknet, zkSync Era native AA) should preserve this distinction explicitly.
Public references
[abstractcardexpostmortem2025]— Abstract Chain core-contributor public statements and community-side incident timeline for the 2025-02-18 Cardex session-signer-key leak.[theblockcardex2025]— The Block reporting on the Cardex exploit including the ~$400K loss, ~9,000 affected accounts, and shared-session-signer / frontend-inlined-key root cause.[decryptcardex2025]— Decrypt reporting on the Cardex incident including the timeline and Abstract Chain attribution.[cryptorankcardex2025]— CryptoRank summary covering the $400K crypto breach figure and the Cardex / Abstract Chain operator response.[quillauditscardex2025]— QuillAudits hack analysis walkthrough of the buyShares-then-transfer-then-sell extraction pattern and the session-signer scope-enforcement detail.[playtoearncardex2025]— PlayToEarn defender-oriented summary including the affected-asset-class detail (ETH only; ERC-20 and NFT positions out of session scope and protected).[itcuacardex2025]— Industry press coverage of the Cardex "lost key" framing.[coinmarketcapcardex2025]— CoinMarketCap incident report on the Abstract / Cardex breach.[cryptopotatocardex2025]— CryptoPotato L2-platform-incident summary.[bitcoinethcardex2025]— Bitcoin Ethereum News six-figure-loss summary.
Citations
[abstractcardexpostmortem2025]— Abstract Chain core-contributor sources; primary source for the operator-side incident timeline and the shared-signer / frontend-inlined-key root cause.[theblockcardex2025]— The Block; primary public-press source for the $400K loss, 9,000 affected accounts, and root-cause framing.[decryptcardex2025]— Decrypt; secondary source for the timeline and attribution framing.[quillauditscardex2025]— QuillAudits; primary source for the buyShares / transfer / sellShares extraction-pattern walkthrough.[playtoearncardex2025]— PlayToEarn; primary source for the in-scope-asset-class restriction (ETH only; ERC-20 / NFT protected by session scope).[cryptorankcardex2025]— CryptoRank; secondary source for the cumulative-loss aggregation.[itcuacardex2025]— Industry press; secondary source for the operator-response summary.[coinmarketcapcardex2025]— CoinMarketCap; secondary source for the public-facing summary.[cryptopotatocardex2025]— CryptoPotato; secondary source for the L2-platform-incident framing.[bitcoinethcardex2025]— Bitcoin Ethereum News; secondary source for the six-figure-loss summary.
Discussion
Cardex 2025-02-18 is OAK's canonical case for the dApp-side session-signer-key compromise sub-pattern of T13.003 at v0.1 freeze. The case is operationally instructive in three distinct dimensions: (a) it cleanly separates the smart-account-side scope-enforcement surface (which performed per-spec and protected ERC-20 / NFT positions) from the dApp-side session-key-storage surface (which was the load-bearing failure), (b) it demonstrates the multiplicative blast-radius effect of the shared-signer architectural anti-pattern, and (c) it provides one of the fastest detection-to-containment timelines in the OAK 2024–2025 corpus thanks to the chain-native session-key registration surface and the Cardex / Abstract-Chain operator coordination.
The structural significance for the T13.003 sub-Technique is the empirical demonstration that the four T13.003 sub-patterns (a, b, c, d) are operationally independent failure surfaces. Cardex was a clean sub-pattern (a) failure with a sub-pattern (d) multiplier (single shared signer = within-application reuse): the smart-account-side scope-enforcement (sub-pattern (b) and sub-pattern (c) defences) worked correctly. Defender practice for the full T13.003 surface should treat the four sub-patterns as independent control surfaces and should not assume that fixing one closes another. The Cardex case is the strongest piece of 2024–2025 corpus evidence that this independence is operationally real.
The detection-to-containment timeline is one of the fastest in the OAK 2024–2025 corpus: ~30 minutes from first suspicious transaction to identification of Cardex as the source; ~few hours from identification to multi-layer containment (application access blocked, session-revocation site deployed, Cardex contract upgraded). The structural enabler was the chain-native session-key registration surface (Abstract Chain's smart-account model surfaces session-key registrations on-chain as discrete, observable events) coupled with the ZK Stack's contract-upgrade primitives. Defender practice for session-key-consuming dApp ecosystems should pre-position session-revocation infrastructure as a first-class incident-response asset, alongside protocol pause / circuit-breaker patterns documented for T9 / T10 / T13.001 incidents.
The bounded session-key-scope dividend is the corpus-level finding that warrants explicit propagation to the OAK Mitigations layer. Cardex's session scope was tight; ERC-20 and NFT positions were structurally protected even under total signer compromise. This is the strongest evidence in the 2024–2025 corpus that session-key scope-tightness is operationally meaningful as defence-in-depth even when the signer key is fully compromised. Defender practice for session-key UX should evaluate scope-tightness as a first-class control independently of signer-key-storage hygiene, and should explicitly compute the realised-loss bound under scope-tightness assumptions as a first-class user-communication item.
The case is structurally distinct from the Loopring 2024 case at examples/2024-06-loopring.md, which sat in T11.002 (Wallet-Software / Signing-Surface Compromise) broadly construed and the smart-wallet-recovery-flow sub-class within T13's account-abstraction scope. Loopring 2024's compromised artefact was a smart-wallet recovery-flow authentication step (the 2FA-equivalent that gates guardian-initiated recovery); Cardex 2025's compromised artefact is a smart-account-registered session signer (the gameplay-time delegated key authorising in-scope operations). The two artefacts are structurally distinct and the two cases anchor different sub-classes within T13's account-abstraction scope. Contributors writing future T13 worked examples should preserve the artefact-level distinction.
For OAK's broader credibility, including Cardex 2025 in v0.1 closes a structural gap: T13.003 (Session-Key Hijacking) at v0.1 was thin on canonical worked-example anchors despite the class being well-characterised in vendor advisories and academic taxonomy. The Cardex case is the cleanest 2025 worked example for the dApp-side session-signer-key compromise sub-pattern, and provides contributors writing future T13.003 worked examples (sub-patterns (b), (c), (d) in particular) with a tone-and-structure precedent for sub-pattern-specific worked-example framing.