Reproduced Exploit
Etherspot CredibleAccountModule — session-key approval drains the wallet
Session-key call validation allows any ERC20 approve target. The key owner chooses itself as spender, then calls transferFrom to remove every token held by the smart wallet rather than only the locked allocation.
Chain
Other
Category
access-control
Date
Jan 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 62847-c-01-sessionkey-owner-can-drain-the-smart-wallet-shieldify-n. Standalone Foundry PoC and full write-up: 62847-c-01-sessionkey-owner-drain-smart-wallet_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-owner-check · vuln/logic/missing-validation
Reproduction: local synthetic Foundry reduction; the complete passing trace is in output.txt.
Key info#
| Field | Value |
|---|---|
| Loss | A session key approves itself and transfers all 1,000 synthetic wallet tokens. |
| Vulnerable contract | CredibleAccountModule.validateApproval in test/62847-c-01-sessionkey-owner-drain-smart-wallet.sol |
| Attacker EOA | 0x1111111111111111111111111111111111111111 |
| Attack contract | Exploit |
| Attack tx | Local Foundry Exploit.run() |
| Chain · block · date | Ethereum model · block 0 · synthetic |
| Compiler | Solidity ^0.8.24 |
| Bug class | Unrestricted ERC20 approve spender |
TL;DR#
Session-key call validation allows any ERC20 approve target. The key owner chooses itself as spender, then calls transferFrom to remove every token held by the smart wallet rather than only the locked allocation.
Background#
CredibleAccountModule sessions are intended to constrain both callable selectors and token amounts. approve is safe only when the spender is the module and the amount is bounded by the session.
The vulnerable code#
function validateApproval(address /*spender*/, uint256 /*amount*/) external pure returns (bool) {
// @> VULN: approve calls are accepted without requiring the module as spender.
return true;
}
See the complete reduction in test/62847-c-01-sessionkey-owner-drain-smart-wallet.sol.
Root cause#
The selector allowlist treats all approve calls as equivalent and does not decode the spender (or amount). A valid session signature consequently grants an unlimited delegated-transfer primitive.
Preconditions#
- The smart wallet holds ERC20 tokens.
- A session key can submit an
approvecall. - The validator does not require the module as spender or enforce the locked amount.
Attack walkthrough#
Exploit.run()mints 1,000 units toSmartWallet.- The session call approves the attacker contract for 1,000 units.
transferFrommoves the full balance; the passing assertion is at output.txt:4.
Diagrams#
Remediation#
Decode every approve call, require spender == address(CredibleAccountModule), and cap the approved amount to the session's locked token amount. Apply the same validation to batch execution.
How to reproduce#
cd evm-hack-registry/62847-c-01-sessionkey-owner-drain-smart-wallet_exp
forge test -vvvvv
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 62847-c-01-sessionkey-owner-drain-smart-wallet_exp (evm-hack-registry mirror).
- AuditVault finding: 62847-c-01-sessionkey-owner-can-drain-the-smart-wallet-shieldify-n.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.