TL;DR
At 04:38:47 UTC on September 15, 2026, 2,900 rsETH left a Gnosis Safe on Ethereum in a single transaction. The Safe's core contracts were never touched. The hole was in a bespoke router sitting behind a module the owner had enabled on the Safe. The router's authorization check returned true whenever the call target was the router's own address, according to the exploit reconstruction filed with DeFiHackLabs. That one short-circuit let an unauthenticated caller nest a second call inside the router's trusted identity, reach a DELEGATECALL executed in the Safe's own context, and push the Safe's Aave-wrapped rsETH into a Uniswap v4 pool the attacker had built minutes earlier with a malicious hook. Then the attacker lost it. They broadcast the exploit to the public mempool, and an MEV bot labeled Yoink took the whole position in block 25980525, keeping 2,882.37 rsETH and paying 18.93 ETH to the block builder to win the slot. Kelp DAO froze the receiving address for 24 hours. A module is a standing grant of the Safe's own identity, and it is only as strong as the caller check on whatever contract fronts it.
What Is a Safe Module, and Why Does One Carry This Much Power?
A Safe module is a contract the owners whitelist to move funds without collecting signatures. Normal Safe execution requires the threshold of owner signatures on every transaction. A module skips that entirely: once enabled, it calls execTransactionFromModule and the Safe performs the action as itself, no quorum, no delay. This is how people automate a looped collateral position or a rebalancing strategy without signing every leg by hand.
The Safe contracts are among the most reviewed code on Ethereum, and here they did exactly what the specification says: they executed an instruction from a module the owner had enabled. Nothing about this incident indicates a flaw in Safe.
The danger sits one layer out. Enabling a module is a permanent delegation of the Safe's authority to another contract's judgment, which means you have also delegated to every caller that contract is willing to trust. We made this point after the SquidRouterModule incident took $3.2M out of a Safe, and the shape repeats here almost exactly.
The module in this case was wired into a strategy router at 0x4f0055926c839D1d960a82CBF84E2eE933958ebC. That contract had been live for over a year. Its on-chain history is 284 transactions, overwhelmingly routine Multicall calls from a single keeper address. Nothing in that history looks like an incident waiting to happen, which is the problem with judging a contract by its traffic.
How Did the rsETH Safe Drain Actually Work?
The router exposed multicall(address _contract, bytes[] _data), and gated it with an internal _isAuthorized check. That check had one fatal branch: it returned true whenever _contract equaled address(this), per the DeFiHackLabs reconstruction. The intent was almost certainly convenience, letting the router batch calls into itself.
The effect was an authorization bypass with a loop in it.
An attacker calls multicall and points it at the router. The check sees the router's own address and waves it through. The payload inside that call is another multicall, this time aimed at the enabled Safe module, and it now executes with the router's identity rather than the caller's. The module, seeing a request from the contract it trusts, calls into the Safe. The final instruction is execTransactionFromModuleReturnData with operation=1, which is DELEGATECALL: the Safe runs the supplied code in its own storage context, holding its own assets.
At that point the caller is the Safe, for all practical purposes.
Three distinct contracts sit in that chain and it is worth keeping them apart: the router that holds the flawed check, the module the Safe has enabled, and a recipe executor that is the actual DELEGATECALL target. The PR author states plainly that the router and the Safe module are two genuinely separate contracts rather than two descriptions of one.
This is CWE-863, Incorrect Authorization: the check ran, and the check was wrong. It is a meaningfully different failure from forgetting the check altogether, because a reviewer skimming for missing modifiers sees a guard and moves on.
The extraction leg used Uniswap v4's hook system. A v4 hook is a contract attached to a pool that runs before and after pool operations, so a pool creator can insert arbitrary logic into a swap or a liquidity change. The attacker deployed a pool pairing rsETH against a token they had minted and named "Permissionless Attacker Token," then used the DELEGATECALL to add roughly 2,900 of the Safe's aEthrsETH to that pool as liquidity. The hook unwrapped the Aave position into raw rsETH on the way through and forwarded it out. On-chain, the Safe also received the pool position NFT, token ID 404329, valued at 999 wei. The victim was left holding the receipt.
The Attack, Step by Step
| # | Block / time (UTC) | Actor | Action |
|---|---|---|---|
| 1 | Before Sept 15 | Safe owner | Enables a strategy module backed by the router at 0x4f0055, delegating the Safe's execution authority |
| 2 | 25980524 | Attacker | Deploys helper contract 0x10605eE48Ff962952C966277A5D2dac0A0705Cb1 and calls Prepare on it, staging the malicious v4 pool and hook |
| 3 | Mempool | Attacker | Broadcasts the triggering call in the clear |
| 4 | 25980525, 04:38:47 | MEV bot Yoink | Submits the same call with higher priority and lands first |
| 5 | 25980525 | Router | _isAuthorized short-circuits on the self-referential target |
| 6 | 25980525 | Module and Safe | DELEGATECALL executes inside the Safe; 2,900 aEthrsETH moves to the v4 pool against 2,905.81 PAT |
| 7 | 25980525 | Hook | Unwraps the Aave position; 2,900 rsETH exits the Safe |
| 8 | 25980525 | Yoink | Keeps 2,882.37 rsETH, swaps 17.63 rsETH for 18.95 ETH, forwards 18.93 ETH to the Ultrasound Money builder |
| 9 | 25980871, 05:47:59 | Attacker | Runs the play again through a second helper and nets 23.69 ETH |
| 10 | 25980912 to 25980942 | Attacker | Bridges the salvage out across seven LI.FI transactions |
Why Did an MEV Bot End Up With the Money?
Because the attacker sent a permissionless exploit through a public mempool, which is a race they had no way to win.
Generalized front-runners watch pending transactions, simulate them, and resubmit anything profitable with a higher priority fee. The usual defense is a private relay, or an exploit that only the attacker's key can trigger. This attacker used neither. The helper contract at 0x10605e shows exactly one top-level transaction on record, the Prepare call the attacker sent in block 25980524. The drain itself arrived as an internal call inside Yoink's transaction, which is only possible because the helper's trigger was reachable by whoever got there first. The attacker built the weapon, loaded it, left the trigger exposed, and a bot pulled it.
The economics of the capture are visible in the trace. Yoink swapped 17.63 rsETH for 18.95 ETH through the Uniswap v4 Pool Manager and forwarded 18.93 ETH, about $46,000 at the time, to the Ultrasound Money builder. The remaining 2,882.37 rsETH went to 0xC70f00CD7E461686b04B0E912E309becA8b80ea0. Call it 0.6% of the haul spent to guarantee inclusion.
We have written before about an MEV bot losing $7.5M to a dangling approval. The machinery cuts in both directions. It is worth being precise about what happened for the victim, though: the funds still left the Safe. The bot is not a rescuer. As of publication the 2,882.37 rsETH sits at an address Kelp DAO paused rather than an address the owner controls.
Which Figure Is Right: $7.73M, $7.8M, or 2,900 rsETH?
Use 2,900 rsETH.
Contemporary reports landed between $7.73M and $7.8M, and both are defensible, because rsETH moved and every outlet priced the same tokens at a different moment. Blockfence put it at $7.8M; Cryptopolitan cited Blockaid data for $7.73M. The token count does not drift. The transaction shows 2,900 aEthrsETH leaving the Safe, 2,900 rsETH withdrawn from Aave, then 2,882.37 rsETH to the recipient address and 17.63 rsETH swapped for gas money. Those two legs sum to exactly 2,900.
One correction worth recording, since the figure is now in circulation. Cryptopolitan's report attaches transaction hash 0xe73c28b90b23d61faf8a6b37e94f6d0551441492d47e9ce768b604d5e42217d3 to the incident. That transaction is a 2,153 ETH deposit into the same Safe on July 23, 2026, 54 days before the exploit. It is the funding of the position, not the theft of it. The exploit transaction is 0x0e7680b06cb8a6f86c149d9ba90d98e3d334e7b072dde03909d43fcfd98a8705. The coincidence that made the mix-up easy is that Blockaid's loss estimate was also quoted as 2,153 ETH.
What Did the Attacker Actually Get?
About 23.69 ETH.
Sixty-nine minutes after losing the position to a bot, the attacker deployed a second helper and ran the authorization bypass again in block 25980871 at 05:47:59 UTC. The transaction reverted in places and completed anyway, ending with 23.689 ETH, roughly $57,800 at the time, forwarded to the attacker's externally owned account at 0x0dC2c5D6b05A317076CF501f7E7be36a5dfe9b66. Over the following minutes that balance went out through LI.FI in seven bridge transactions of one to four ETH each. The account was originally funded through RAILGUN's relay adapter, which is consistent with someone who planned the exit before the entry and then had almost nothing to exit with.
Against a 2,900 rsETH position, the attacker realized under 1% of what their own exploit produced. Public reporting has not established which positions the second run drew on, and nobody has published an attribution for the attacker. The owner of the Safe has not been identified, so whether this was a DAO treasury or one large individual position is still open. Nor has anyone said whether that router was ever audited, or by whom, which is the question most worth asking about a contract that carried this much delegated authority for a year. The operator behind Yoink has made no public statement about the 2,882.37 rsETH.
What Does This Generalize To?
An audit certifies the code you showed it, on the day you showed it. It says nothing about the module you enabled afterward, or the router that module trusts, or the branch in that router's permission check that treats its own address as a credential.
A few patterns are worth carrying out of this one.
Self-reference is not a permission. A check that accepts address(this) as an authorized target is not narrowing anything, because the contract's own entrypoints are the exact surface an attacker wants to reach. Batching helpers reintroduce this constantly: the batcher needs to call itself for legitimate composition, the author writes the shortest branch that allows it, and the branch becomes a universal key for anyone who can hand the batcher a payload.
Delegated execution compounds. The Safe trusted the module. The module trusted the router. The router trusted anything pointed at the router. Each of those links was defensible read alone. Trust is not transitive, and privilege chains rarely get reviewed end to end, because each contract has a different author and a different review date.
DELEGATECALL from a module is the top of the ladder. operation=1 means the payload runs inside the Safe, with the Safe's storage and the Safe's balances. There is no narrower thing to escalate to after that. Any module path that can reach a caller-supplied DELEGATECALL deserves treatment as an owner key, because functionally that is what it is.
Operator Takeaways
-
Inventory every enabled module on every Safe you hold, today. Enabling is a one-time transaction; the grant is permanent until revoked.
getModules()on the Safe is the authoritative list, and it should match a list a human maintains and can justify. -
Review the module's dependencies, not only the module. The vulnerable code here was in a router the module called, not the module itself. Trace the full privilege chain: who can call the module, what the module calls, and what those contracts accept from unauthenticated callers.
-
Grep every batching contract for self-referential authorization. Any branch that returns true on
target == address(this),msg.sender == address(this), or an equivalent identity comparison is a bypass unless the nested payload is separately constrained. If your router needs to call itself, allowlist the specific selectors it may call, not the address. -
Treat caller-supplied
DELEGATECALLas unreachable by default. If a module forwards arbitrary calldata intoexecTransactionFromModule*withoperation=1, the caller check on that path is the entire security model. Hardcode the delegate target, or refuseoperation=1in that path. -
Sunset stale automation. The router had run for more than a year against a single keeper address. A module that a strategy no longer needs is exposure with no offsetting benefit, and its low transaction count makes it easy to forget.
-
Alert on your own privilege surface, not only on your balances. An external caller invoking a router whose lifetime traffic is 284 transactions from one keeper address is a rule you can write today.
That last takeaway is where the two halves of this incident meet. The drain was atomic, one block after the staging, so nothing alerts ahead of it. The condition, though, sat in the open for a year: a module holding DELEGATECALL reach over a Safe carrying 2,900 rsETH, fronted by a router whose lifetime traffic was 284 transactions from a single keeper. An unrecognized address calling that router is a rule you can write today, and the attacker's second run, 69 minutes later from a new helper contract, would have tripped it. Tripwire is built for exactly that surface: the standing grants on your Safes, and the first caller that does not belong. The flawed branch was findable before any of it, which is what a review of the router is for. Running a module over a funded Safe is two jobs: read the contract before you enable it, then alert on the first caller it does not recognize.
Frequently Asked Questions
Was Gnosis Safe hacked?
No. The Safe contracts executed a valid instruction from a module the owner had whitelisted. Security firms including BlockSec, Blockaid and SlowMist traced the flaw to an auxiliary contract the wallet owner had authorized, not to Safe's core infrastructure, as summarized by Crypto Economy.
Was Kelp DAO or the rsETH protocol compromised?
No. Kelp DAO stated, in remarks reported by both Blockfence and Crypto Economy, that its contracts remained secure and rsETH stayed fully backed, and it applied a 24-hour transfer pause to the address holding the proceeds: "Out of an abundance of caution, we've placed that address under a temporary 24-hour pause. During this window, rsETH cannot move in or out of it." Minting, redemptions and integrations continued.
Who has the funds now?
2,882.37 rsETH went to 0xC70f00CD7E461686b04B0E912E309becA8b80ea0, the address Kelp DAO paused. The MEV operator, not the attacker, controls that leg. There is no public statement from the Yoink operator about returning it as of publication.
How was the authorization check wrong, exactly?
The router's internal check returned true whenever the call target equaled the router's own address, so a caller could satisfy it by aiming the outer call at the router and hiding the real target in the nested payload. Metaverse Post reports that the entrypoint then forwarded fully caller-supplied calldata into the Safe's execTransactionFromModuleReturnData with operation=1, without gating the external caller.
Could an audit have caught this?
Yes. The flaw was a single branch in a permission check, reachable through a public multicall entrypoint. Finding that branch is the core of what a smart contract review does, and self-referential authorization inside a batching path is a pattern reviewers are trained to look for.
The harder question is scope. No audit of this router has been made public, and it held delegated authority over the Safe for more than a year. Owners audit the protocol, then attach a module nobody scoped. The scope that matters is everything the Safe's authority can reach.
Sources / References
- Ethereum Transaction Hash: 0x0e7680b06c... | Etherscan (the exploit transaction, block 25980525)
- Add RsETHSafeModule exploit PoC - nested self-referential multicall authorization bypass drains Gnosis Safe via DELEGATECALL by BhariGowda · Pull Request #1262 · SunWeb3Sec/DeFiHackLabs
- Exploit Drains $7.8M In rsETH From User's Safe Wallet Through Compromised Strategy Executor | Metaverse Post
- MEV bot front-runs $7.8M rsETH exploit on Ethereum
- Attacker Drains 2,900 rsETH From Gnosis Safe Wallet in Major Ethereum Heist - Crypto Economy
- Kelp DAO freezes funds as Gnosis wallet suffers $7.8M exploit - Cryptopolitan
- MEV Bot Takes $7.8M From rsETH Exploit
- CWE - CWE-863: Incorrect Authorization (4.20)
- Address 0x4f0055926c839D1d960a82CBF84E2eE933958ebC | Etherscan (the strategy router)
- Address 0x0dC2c5D6b05A317076CF501f7E7be36a5dfe9b66 | Etherscan (attacker EOA, RAILGUN-funded)



