Crystal DAO

Objective

The Crystal DAO is a transparent and non-profit DAO whose mission is to gather funds to support the development of different public goods in the Ethereum ecosystem. These funds are stored in custom treasury contracts that follow the ERC1176 minimal proxy standard, and are controlled by one DAO admin.

One of such treasuries was recently deployed, and has reached the target amount of 100 ETH in its balance. Therefore, the DAO admin has tried to retrieve the funds for their subsequent donations. However, the admin's signature is not being recognized by the treasury clone contract, and the funds are now stuck, putting the DAO's reputation at risk.

Can you help the DAO admin to retrieve the funds?

📌 Rescue 100 ETH from the DAO treasury.

Initial context

  • You will be in control of the whitehat address.

  • The whitehat address has an initial balance of 0 ether.

🗒️ Concepts you should be familiar with (spoilers!)

The contracts that you will hack are:

The test script where you will have to write your solution is:

Writeup

First I poked around and found owner is always the zero address. Why is that?

After some debugging, I found that the sstore in initialize() fails to update the owner:

    function initialize(address _owner) public initializer {
        // EIP712 init: name DaoWallet, version 1.0
        __EIP712_init("DaoWallet", "1.0");
        // postInit: set owner with gas optimizations
        assembly {
            sstore(0, _owner)
        }
    }

The storage slot is wrong here. Note that the contract inherits from Initializable and EIP712Upgradeable:

contract DaoVaultImplementation is Initializable, EIP712Upgradeable {
    ...
}

In multiple inheritance scenario, the storage variables from Initializable are stored in storage slots first, then EIP712Upgradeable, then DaoVaultImplementation. Use forge inspect to verify this fact:

PoC

Last updated