How we rescued funds from a bricked Safe with EIP-1271

tl;dr;*
This week, a Yearn periphery contract used to manage some treasury assets was compromised, resulting in a bricked Gnosis Safe. Yearn used EIP-1271 as a workaround to transfer assets from the bricked multisig to safety.*
No user funds are affected

Background

Yearn has a secondary multisig for limited operations, known as "yTrades", meant to be a fast-acting approver for performing one-off time-sensitive operations like swaps. This multisig was the victim of a complex attack this week which rendered it inoperable, and trapped approximately $1,000,000 worth of assets. At the root of this attack is a lesser-known feature of Safe multisigs known as a "Guard".

What is a Guard and how did Yearn's yTrades multisig use one?

In short, a Safe Guard is a custom contract which a multisig can optionally add to their Safe to run on-chain checks both before and after every transaction execution. More information here in the Safe docs.

In 2021, Yearn developed and implemented a custom Safe Guard on the yTrades multisig as part of a system designed to help prevent MEV (that system is outside the scope of this blog post but you can read more about it here).

Importantly, Yearn's implementation included a governance role and an executors whitelist, blocking transaction execution if not on the whitelist.

The Attack

Due to an oversight, the team failed to set the protocol's main multisig (yChad.eth) as the governance role on the Guard contract. Instead, governance remained set as the original deployer EOA, which was a vanity address generated using the Profanity tool (see here for more details about the Profanity exploit).

An attacker recently discovered this detail and used the exploit to compromise the wallet, taking control and executing a transaction designed to brick the yTrades multisig. By taking over the governance and management roles, the attacker was able to empty the Guard's executor whitelist.

Since the executors list was now empty, all transactions from the multisig fail the Guard checks and therefore revert, rendering yTrades completely inoperable. Since removing the Guard itself requires a successful transaction, that too is now impossible.

See our appendix for some tips on how to safely use a Guard.

The Recovery

After some hours of trying to work around Stealth Safe Guard logic to no avail, we received a tip on the possibility of using EIP-1271 for order creation in CoWSwap.

EIP-1271 orders in CoWSwap using a Safe

The gist of it is that, if you previously approved a token on CoWSwap's relayer contract, you can approve a CoWSwap order using off-chain signatures alone - no requirement to publish an on-chain transaction.

All transactions above were created using EIP-1271
All transactions above were created using EIP-1271

Let's walk through the steps:

  • A CoWSwap EIP-712 Order is created and hashed

  • The hashed order is added to a Safe's EIP-712 message and hashed

  • Needed owners sign the Safe's message

  • All signatures are sorted least to greatest and concatenated

  • An order in the CoWSwap API is created with the original EIP-712 order plus the concatenated signatures

  • CoWSwap's API accepts the order and inserts it in the orderbook

  • Solvers compete in an auction for the best settlement

  • Order(s) settle on-chain

The real magic in this process: everything happens off-chain until the auction settlement. As you can see here, the CoWSwap settlement contract checks Safe's isValidSignature(), and as long as the Safe message hash was signed by enough owners, it is accepted.

Future work with EIP-1271

After seeing the power of EIP-1271, we would like to push for a wider adoption of the standard. Safe's smart contract and website will soon allow message signing through their API. This means that if the protocol you want to interact with supports the standard, after approvals you may be able to interact completely gas-free.

We will continue improving our DAO trades settlement with CoWSwap and evolve our order creation through EIP-1271, and are also planning to add support for EIP-1271 interactions to Yearn's V3 vaults.

Feature request for Safe

We would like to take this opportunity to publicly asking the Safe team to add a remove guard functionality to the safe using EIP-1271. If owners want a guard removed, there should be a way to do it through signatures.

Bounty

Although the majority of the Safe's tokens were recovered, there remain a few tokens that were not approved on CoWSwap's relayer, and thus are locked in yTrades. We are currently treating the attacker as a white hat, and until 6/25 will give them benefit of the doubt. We have created a bounty contract for the guard owner to take action, which can be found here.

Thanks

We would like to thank the on-chain sleuths who contacted us to provide information about the attacker, as well the engineers from CoWSwap, Safe and elsewhere who aided our work to create these transactions. Furthermore, thank you to everyone developing infra and tooling for Ethereum—even with tooling this was difficult, and without it this would have been impossible.

Appendix

Safely using a Safe Guard

IMPORTANT: If you are using a safe guard, notice that:

  • Guard removal goes through a transaction execution of the safe

  • Safe can only be upgraded through a tx execution of the safe.

  • Owners can't execute a transaction from the safe without the guard's blessing

  • If the guard reverts, the transaction doesn't settle

A broken/compromised guard means a bricked safe.

We repeat...

A broken/compromised guard means a bricked safe.

Failures on our side

Looking back, there were a few issues which should have mitigate this attack

  1. Stealth Safe Guard governance was never set to yChad

  2. Stealth Safe Guard had a overrideGuardChecks setter which skipped the pre-transaction logic

  3. Instead of removing the guard (as we ended up not using it), we simply set overrideGuardChecks = true

Subscribe to Yearn Finance Engineering Blog
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.