# Hats Finance CTF#2 Write-Up

By [Not a bug](https://paragraph.com/@not-a-bug) · 2022-10-18

---

Hats Finance is a decentralized bug bounty platform. One big difference that this platform has over bug bounty platforms for web3 is that the payments that are rewarded are firstly locked in vaults and also use the project’s/protocol’s token. Success of the project, which in some respects means a secure project, means the bounty is also increased.

Hats Finance also publishes Capture The Flag (CTF) challenges that are good practice for bounty hunters. They reward those with the top submissions and for CTF#2, the top 10 were able to claim [1000 DAI along with an NFT](https://twitter.com/HatsFinance/status/1576998796004773888). I was a bit too late with getting started with the CTF so my submission didn’t make it in time, but I went ahead and used it as practice anyway.

[https://github.com/hats-finance/vault-game](https://github.com/hats-finance/vault-game)

Capture the Flag
----------------

_The contract_ `Vault.sol` _is an ERC4626-like vault customized to be used with ETH. It allows anyone to deposit ETH in the vault and get shares corresponding to the amount deposited. The shares are an ERC20 which can be freely used by users, functioning effectively just like_ [_Wrapped ETH_](https://weth.io/)_. The shares can also be redeemed at any time for the corresponding underlying amount of ETH._

The Hats Challenge
------------------

_The_ `Vault.sol` _is deployed with the contract owning 1 ETH of the shares. Your mission is to capture the flag by emptying the vault, then calling_ `captureTheFlag` _with an address you control to prove that you have succeeded in completing the challenge, so that_ `vault.flagHolder` _returns your address._

Contract Analysis
-----------------

It looks like the main contract only has two functions, the constructor and a function to capture the flag, nothing looks out of the ordinary here, but it does use a custom implementation of the [ERC4626](https://ethereum.org/en/developers/docs/standards/tokens/erc-4626/). With this, we can look for the differences in the contracts and gain a sense of what kind of vulnerabilities hide in the contract. The main areas we will look at are the `_deposit` and `_withdraw` functions.

![Source: vault-game/ERC4626ETH.sol at main · hats-finance/vault-game (github.com)](https://storage.googleapis.com/papyrus_images/b21f1574a538b9775a9b138b9004feb4efe3aa27d322c8c09b1a59848c99837b.png)

Source: vault-game/ERC4626ETH.sol at main · hats-finance/vault-game (github.com)

The way this implementation differs from the proposed standard is that instead of shares, it uses actual ETH instead of shares like how the EIP states. Furthermore, if we dig into **line 28** of `_withdraw`, we can see that it uses the balance of the contract in order to calculate the excessETH that should be sent back to the owner on **line 33-34**. This wouldn’t be a problem because `Vault.sol` doesn’t implement `recieve` or `fallback` so that means there is no way to send ETH to this contract…right? Wrong, you can use `selfdestruct(target)` in a contract to force the ETH and tokens into another address or contract. You can read more about it here:

[https://solidity-by-example.org/hacks/self-destruct/](https://solidity-by-example.org/hacks/self-destruct/)

Now that we know a way of manipulating state in the contract, is there a way of continually doing this action? The answer is yes, with a re-entrancy attack! If we were to send ETH from a contract and have a receive function call `Vault.sol` again, we can repeat the action of sending excessETH to the owner because the variable is written above where the re-entracy happens.

![](https://storage.googleapis.com/papyrus_images/36c9cb6aef0ccc382e5ef9fdfa505e6c36e776dc5b44cebd8464b2e6bd21b47d.png)

Attack Scenario
---------------

1.  Deploy a contract with 1 ETH that will `selfdestruct` to the `Vault` contract.
    
2.  Deploy a contract that can call the `withdraw` function in the `Vault`. This contract should also have a `recieve` function that will re-enter the `withdraw` function in order to drain the vault.
    
3.  Withdraw using the deployed contract and drain the vault.
    
4.  Capture the Flag
    

Visualization
-------------

In order to help solidify the concept of re-entrancy attacks, I _attempted_ to create some diagrams of how it works in this particular case.

[https://imgur.com/a/fjmQ7gD](https://imgur.com/a/fjmQ7gD)

![Setting up the contracts](https://storage.googleapis.com/papyrus_images/b6a0f7db1320f029829c3f48217c9f3232de4637630bfac1e8789ed05dc4701d.png)

Setting up the contracts

![Self Destruct | Solidity by Example | 0.8.13 (solidity-by-example.org)](https://storage.googleapis.com/papyrus_images/5023488d881b6ee54fa3c03b25ffc561fd7b8fbfd068a9d7c98887ae7b54818b.png)

Self Destruct | Solidity by Example | 0.8.13 (solidity-by-example.org)

![](https://storage.googleapis.com/papyrus_images/7df491f6a452ad5866cbeb2a51ecc2ef7ab8356eaa2e18a3ff73281013fb77d2.png)

![](https://storage.googleapis.com/papyrus_images/563de103fada9242eb38c0ca7dc6dc7d0e9eb519ef7857ecedf04855ed9f5d53.png)

![](https://storage.googleapis.com/papyrus_images/26a50a9d737e6186439c58a5c3f6dad233b68f80b8d4422b872d0713069384cc.png)

![](https://storage.googleapis.com/papyrus_images/4212f9c683a3a6cac8a638ff83ec62826f15e59a773d889178410cb202f71b24.png)

After the re-entrancy is done the excess ETH gets sent back to the owner.

![](https://storage.googleapis.com/papyrus_images/89a8694306df25edbf93d6c1fb8ff5332a960873cd364f94dbafc8f1a3caab13.png)

![](https://storage.googleapis.com/papyrus_images/75dc5f81e2cfecdaa3d5bc31ebb50003d9005f0ee0f9be36e8389b17e0a593f3.png)

![](https://storage.googleapis.com/papyrus_images/f3e3db42060d0868476b59a3fb39162a8023577f78aad103356298d26b9af681.png)

Prevention
----------

There are a couple ways of preventing this from happening:

1.  Add a `non-reentrant` modifier to the withdraw function.
    
2.  Add a receive/fallback function to the vault contract and instead of using the contracts balance, a variable can be incremented and decremented when withdrawing or depositing into the vault.
    
3.  Move the calculation for `excessETH` underneath the first transfer in the `_withdraw` function.
    

Below is a link to my solution to the CTF. I also included Foundry tests because I’m a foundry maxi.

[https://github.com/BoxedFruits/hats-finance-ctf-2](https://github.com/BoxedFruits/hats-finance-ctf-2)

---

*Originally published on [Not a bug](https://paragraph.com/@not-a-bug/hats-finance-ctf-2-write-up)*
