Reproduced Exploit
Etherspot CredibleAccountModule — [H-01] no check for `userOp` / `userOpHash` mismatch nor sender validity
1. validateUserOp(userOp, userOpHash) derives sessionKeySigner from userOpHash and validates params against userOp. 2. There is no check that userOp.sender == msg.sender, and no check that userOpHash is the EIP-4337 hash of that userOp.
Chain
Other
Category
untagged
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: 61411-h-01-no-check-for-userop-and-userophash-mismatch-nor-the-val. Standalone Foundry PoC and full write-up: 61411-h-01-no-check-for-userop-and-userophash-mismatch-nor-the-val_exp in the
evm-hack-registrymirror.
Vulnerability classes: missing-modifier · direct-drain · account-signer · reward-accounting
Reproduction: a self-contained Foundry PoC that compiles & runs in an isolated project with only
forge-std— no fork, no RPC. Full trace: output.txt. PoC: test/61411-h-01-no-check-for-userop-and-userophash-mismatch-nor-the-val_exp.sol.
AuditVault taxonomy — lang/solidity · platform/shieldify · has/poc ·
severity/high · sector/account · sector/stable · sector/staking ·
genome: missing-modifier · direct-drain · account-signer · reward-accounting
Key info#
| Impact | HIGH — an attacker can call validateUserOp directly with a victim's valid (userOp, userOpHash), claim the session key, and brick the real modular account from consuming the session (tokens remain locked / session unusable) |
| Protocol | Etherspot — CredibleAccountModule |
| Vulnerable code | CredibleAccountModule.validateUserOp — recovers signer from userOpHash and mutates state from userOp without binding them or checking msg.sender |
| Bug class | Missing authentication / hash binding on ERC-4337 validation entrypoint |
| Finding | Shieldify Security Review · AuditVault #61411 |
| Report | Etherspot CredibleAccountModule Security Review |
| Source | AuditVault |
| Status | Fixed by the team |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
validateUserOp(userOp, userOpHash)derivessessionKeySignerfromuserOpHashand validates params againstuserOp.- There is no check that
userOp.sender == msg.sender, and no check thatuserOpHashis the EIP-4337 hash of thatuserOp. - Anyone can replay a victim's signed hash with the victim's op data by calling the module directly (not via EntryPoint/SCW), claiming the session.
- The real modular account can no longer execute the session path; locked tokens cannot be released through that session.
The vulnerable code#
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) ... {
if (userOp.signature.length < 65) return VALIDATION_FAILED;
bytes memory sig = _digestSignature(userOp.signature);
address sessionKeySigner = ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), sig);
if (!validateSessionKeyParams(sessionKeySigner, userOp)) {
return VALIDATION_FAILED;
}
// @> VULN: no userOp.sender == msg.sender, no hash(userOp) binding
sessionClaimed[sessionKeySigner] = true;
}
Recommended fix:
+ require(userOp.sender == msg.sender, "sender");
+ // optional but stronger: require(userOpHash == getUserOpHash(userOp), "hash mismatch");
Root cause#
ERC-4337's EntryPoint is expected to be the only caller of validateUserOp and
to supply a correctly bound hash. The module trusted that ambient guarantee
instead of enforcing it. Direct external calls therefore get full session-claim
side effects with attacker-chosen msg.sender.
Preconditions#
- Victim modular account has installed the module and enabled a session key.
- Attacker observes or constructs a valid
(userOp, userOpHash, signature)for that session. - Module is callable externally (not restricted to EntryPoint / account).
Attack walkthrough#
- Victim SCW enables session key with locked tokens for a solver claim.
- A legitimate
PackedUserOperationis prepared for the SCW sender. - Attacker calls
module.validateUserOp(op, hash)directly. - Session is marked claimed; validation returns success.
- Victim SCW's subsequent
executeWithSessionreverts (already claimed). - Locked tokens remain locked — session burned without legitimate release.
Diagrams#
Impact#
Malicious parties can consume other modular accounts' session signatures, preventing legitimate solvers/users from releasing locked tokens via that session. High impact loss-of-liveness / funds-stuck for affected accounts.
Sources#
- AuditVault finding #61411
- Shieldify — Etherspot CredibleAccountModule Security Review
- Vulnerable source: etherspot/etherspot-modular-accounts@d4774db
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 61411-h-01-no-check-for-userop-and-userophash-mismatch-nor-the-val_exp (evm-hack-registry mirror).
- AuditVault finding: 61411-h-01-no-check-for-userop-and-userophash-mismatch-nor-the-val.
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.