ERC-4337 Smart Account Sicherheitsfallen
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Die ERC-4337 Account-Abstraktion verwandelt Wallets in programmierbare Systeme. Der Kernablauf ist validate-then-execute über ein gesamtes Bundle: der EntryPoint validiert jede UserOperation, bevor er eine von ihnen ausführt. Diese Reihenfolge schafft nicht offensichtliche Angriffsflächen, wenn die Validierung zu großzügig oder zustandsbehaftet ist.
1) Umgehung privilegierter Funktionen durch Direktaufrufe
Jede extern aufrufbare execute (oder funds-moving) Funktion, die nicht auf EntryPoint (oder ein geprüftes executor module) beschränkt ist, kann direkt aufgerufen werden, um das Konto zu leeren.
function execute(address target, uint256 value, bytes calldata data) external {
(bool ok,) = target.call{value: value}(data);
require(ok, "exec failed");
}
Sicheres Muster: auf EntryPoint beschränken und msg.sender == address(this) für Admin-/Selbstverwaltungsabläufe verwenden (Modul-Installation, Validator-Änderungen, Upgrades).
address public immutable entryPoint;
function execute(address target, uint256 value, bytes calldata data) external {
require(msg.sender == entryPoint, "not entryPoint");
(bool ok,) = target.call{value: value}(data);
require(ok, "exec failed");
}
2) Nicht signierte oder ungeprüfte Gas-Felder -> Gebührenabfluss
Wenn die Signaturvalidierung nur die Absicht (callData) abdeckt, aber nicht gasbezogene Felder, kann ein bundler oder frontrunner die Gebühren aufblasen und ETH abziehen. Die signierte Payload muss mindestens folgende Felder binden:
preVerificationGasverificationGasLimitcallGasLimitmaxFeePerGasmaxPriorityFeePerGas
Absicherungsmuster: Verwende den von EntryPoint bereitgestellten userOpHash (der die gasbezogenen Felder einschließt) und/oder begrenze jedes Feld strikt.
function validateUserOp(UserOperation calldata op, bytes32 userOpHash, uint256)
external
returns (uint256)
{
require(_isApprovedCall(userOpHash, op.signature), "bad sig");
return 0;
}
3) Stateful validation clobbering (bundle semantics)
Weil alle Validierungen vor der Ausführung laufen, ist es unsicher, Validierungsergebnisse im Contract-State zu speichern. Eine andere op im selben Bundle kann sie überschreiben, sodass deine Ausführung Zustand verwendet, der von einem Angreifer beeinflusst wurde.
Vermeide Schreibzugriffe auf Storage in validateUserOp. Falls unvermeidlich, indiziere temporäre Daten mit userOpHash und lösche sie nach Gebrauch deterministisch (bevorzuge zustandslose Validierung).
4) ERC-1271 replay across accounts/chains (missing domain separation)
isValidSignature(bytes32 hash, bytes sig) muss Signaturen an diesen Contract und diese Chain binden. Das Recovern über einen rohen Hash ermöglicht Replay von Signaturen über Accounts oder Chains.
Verwende EIP-712 typed data (Domain enthält verifyingContract und chainId) und gib bei Erfolg den exakten ERC-1271 Magic-Wert 0x1626ba7e zurück.
5) Reverts do not refund after validation
Sobald validateUserOp erfolgreich ist, sind Gebühren festgeschrieben, selbst wenn die Ausführung später revertet. Angreifer können wiederholt ops einreichen, die fehlschlagen, und trotzdem Gebühren vom Account einziehen.
Für paymasters ist es fragil, in validateUserOp aus einem gemeinsamen Pool zu bezahlen und Nutzer in postOp zu belasten, da postOp revertieren kann, ohne die Zahlung rückgängig zu machen. Sichere Mittel während der Validierung (per-user escrow/deposit) und halte postOp minimal und nicht-revertierend.
6) ERC-7702 initialization frontrun
ERC-7702 erlaubt einer EOA, smart-account Code für eine einzelne tx auszuführen. Wenn die Initialisierung extern aufrufbar ist, kann ein Frontrunner sich selbst als Owner setzen.
Abhilfe: Initialisierung nur bei self-call und nur einmal erlauben.
function initialize(address newOwner) external {
require(msg.sender == address(this), "init: only self");
require(owner == address(0), "already inited");
owner = newOwner;
}
Schnelle Pre-Merge-Checks
- Signaturen mit
EntryPoint’suserOpHashvalidieren (bindet Gas-Felder). - Privilegierte Funktionen auf
EntryPointund/oderaddress(this)beschränken, je nach Bedarf. validateUserOpzustandslos halten.- EIP-712 Domain-Separation für ERC-1271 erzwingen und
0x1626ba7ebei Erfolg zurückgeben. postOpminimal, begrenzt und nicht revertierend halten; Gebühren während der Validierung sichern.- Für ERC-7702 init nur bei Selbstaufruf und nur einmal erlauben.
Referenzen
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


