# Sturdy Finance Hack

By [princ](https://paragraph.com/@princ) · 2023-06-16

---

### Summary

On June 12, 2023, Sturdy Finance was hacked on Ethereum Mainnet. The hackers were able to exploit a vulnerability in the protocol’s borrowing pool, which allowed them to steal around $800K worth of ETH.

### About Project

Sturdy Finance is a decentralized finance (DeFi) protocol that allows users to borrow and lend assets without paying interest. Instead of charging borrowers interest, Sturdy stakes their collateral and passes the yield to lenders. This model changes the relationship between borrowers and lenders to make Sturdy the first positive-sum lending protocol.

To learn more about the Project, check out the official [documentation](https://docs.sturdy.finance/overview/what-is-sturdy).

Vulnerability Analysis and Impact
---------------------------------

### On-Chain Details

`Attacker Address` : [0x1e8419e724d51e87f78e222d935fbbdeb631a08b](https://etherscan.io/address/0x1e8419e724d51e87f78e222d935fbbdeb631a08b)

`Attack Transaction` : [0xeb87ebc0a18aca7d2a9ffcabf61aa69c9e8d3c6efade9e2303f8857717fb9eb7](https://etherscan.io/tx/0xeb87ebc0a18aca7d2a9ffcabf61aa69c9e8d3c6efade9e2303f8857717fb9eb7)

`Attack Contract` : [0x0b09c86260c12294e3b967f0d523b4b2bcdfbeab](https://etherscan.io/address/0x0b09c86260c12294e3b967f0d523b4b2bcdfbeab)

`Sturdy Exploited Contract` : [0x46beA99d977F269399fB3A4637077bB35F075516](https://etherscan.io/address/0x46bea99d977f269399fb3a4637077bb35f075516#code)

### The Root Cause

The root cause of the exploit was a read-only reentrancy present in Balancer's `PoolBalances.sol` smart contract. The vulnerable function was `_joinOrExit(...)` which was marked protected with `non-Reentrant` modifier but was still vulnerable. Since there was an external call involved in the function, this caused the exploit to happen.

![External call made before updating the values](https://storage.googleapis.com/papyrus_images/ad9336fd28f5a31292256da25ebb28308096b02e3adce3ce1622dfc4e55cfb7e.png)

External call made before updating the values

### Attack Process

![Exploiter's plan](https://storage.googleapis.com/papyrus_images/d3f72e23e240b26f7d4d5e376fc630bdbb9c7b7d36d51fb3b1e12acfe37a747b.png)

Exploiter's plan

> **1\. Attacker took a flashLoan of 50,000** `wstETH` and 60,000 `WETH` from AAVE.

![Took flashloan](https://storage.googleapis.com/papyrus_images/8ecd72db483d1b576dd6bd3d510cab782b8ea74bfcd8a0219a51afd5e324d3be.png)

Took flashloan

> **2\. Attacker minted 1023** `steCRV` tokens using `Curve steCRV` Pool by depositing 1100 `ETH`

![ -->  add_liquidity[1100.0 ETH]](https://storage.googleapis.com/papyrus_images/af5b2e8601f59c8f12dc6c0e8fd58124717ac2d450b505899443abfb5b2e2205.png)

\--> add\_liquidity\[1100.0 ETH\]

> **3\. 109,517** `B-stETH-STABLE` tokens were also minted by depositing 50,000 `wstETH` and 57,000 `WETH` in Balancer Pool.

![joinPool of BALANCER to mint the token](https://storage.googleapis.com/papyrus_images/3648ae94f339203cfa5a7d13e905845f56974ca63dbb3f9f86d4c8e4bcaca24b.png)

joinPool of BALANCER to mint the token

> **4\. Attacker borrowed 513 WETH from Sturdy Lending Pool using two tokens as collateral. (1000** `steCRV` **& 233** `B-stETH-STABLE`**)**

9

![borrow(asset = WETH)](https://storage.googleapis.com/papyrus_images/74ec3c2ff807f419ae6dce66e76544a2a2c7eae6e80f8d05c7a3108da1ceb401.png)

borrow(asset = WETH)

> **5\. Increase the price of the** `B-stETH-STABLE`, so that `steCRV` can be set as non collateral. Since now the price of `B-stETH-STABLE` is increased, `PoolBalances.sol/exitPool()` is called.

![entry point of the exploit](https://storage.googleapis.com/papyrus_images/b30a12cbc0e08ca18c00783d1dd457c0fb9d3ce3e0ba87ba6949f7802565ffb4.png)

entry point of the exploit

> **This function calls** `PoolBalances._joinOrExit()`function which is vulnerable to read only vulnerability and does an external call indirectly.

![vulnerable function](https://storage.googleapis.com/papyrus_images/c02f887f7aa997c4e49a358a685a6bcb8641807ce4f33b7dec3f678bb00ce77b.png)

vulnerable function

> **further call-graph is as follows :**
> 
>     PoolBalances._joinOrExit() --> PoolBalances._callPoolBalanceChange() --> AssetTransferHandler._sendAsset() --> Address.sendValue()
>     
> 
> **sendValue() is the function using the solidity native call.**

![external call has been made](https://storage.googleapis.com/papyrus_images/16b3c681a90ca3f3218dc3a0184204a4df0eea5fe8dfe2bdc1acf48864637320.png)

external call has been made

> The call made by the function then used by fallback of the malacious contract to call Sturdy contract `LendingPool.setUserUseReserveAsCollateral()` to set the `steCRV` as non-collateral as after the price manipulation, the protocol considers that only 233 `B-stETH-STABLE` are able to cover the debt. This function then calls the read function `SturdyOracle.getAssetPrice()` which gives false output due to the reentrancy present in the function we discussed above The false output is due to the fact that the value comes directly from the Balancer `PoolTokens.getPoolTokens()` contract.

> **7\. Withdraw the 1000** `steCRV` tokens from pool.

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

withdrawCollateral()

> **8\. Now, since the price of** `B-stETH-STABLE` has come to normal, liquidate the position in Sturdy Pool with 236`WETH` to get back 233`B-stETH-STABLE`.

![liquidationCall](https://storage.googleapis.com/papyrus_images/98d3adc0970bfc02116b33e4121811cb1d3e7facec696e89b03d4a0fb4977366.png)

liquidationCall

> **9\. Give the FlashLoan back.**

`Steps 3-8 are repeated 5 times and are contained in in the exploiter contract as a single function maybe named as "yoink()" This function is called 5 times and contains the logic for steps 3-8.`

### Flow of Funds

Immediately after the exploit the exploiter sent the funds to the tornado cash. So, the current owner of the funds is unknown.

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

### Value impact on the protocol

![Source : DefiLlama](https://storage.googleapis.com/papyrus_images/39b1aae3f3dd6b0dc2003e3b981ec944ecfff5f44f822613e9332e935a25d0aa.png)

Source : DefiLlama

### Incident timelines

On the same day of exploit (June 12, 2023) : Sturdy Finance acknowledged the exploit and tweeted:

![Twitter](https://storage.googleapis.com/papyrus_images/36502b13b83070cce2c726368e152ea275426d0b48a500d1483db9fc522b4cc5.png)

Twitter

They also published a report related to the exploit on June 14, 2023.

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

Twitter

### Learnings from this exploit

*   Proper implementation of the function doing external call should be done.
    
*   Tests covering each and every possibility should be written.
    
*   There should be consistency while viewing the state of the blockchain through any source.
    
*   Functions viewing the state of blockchain should utilize more than one source.

---

*Originally published on [princ](https://paragraph.com/@princ/sturdy-finance-hack)*
