Exploit post-mortem

Sturdy
3 min readJun 30, 2023

--

Overview

This article provides a technical overview of the June 12 exploit and shares more detailed information on next steps.

Background

Sturdy has always taken security seriously, having maintained an active $100,000 bug bounty program and undergone four audits in just over a year.

In February 2023, Sturdy added Balancer’s wstETH/WETH pool as collateral. Though this was a minor addition, we decided to audit the new Balancer oracle contract with Quantstamp (see audit here) and subsequently implemented changes according to the audit’s findings.

Part of the integration entailed building an oracle for the LP token. The oracle works by taking the minimum price of the tokens in the pair and multiplying them by getRate(); this value represents the total underlying token amounts divided by the total supply of the LP token. During development, Sturdy was made aware by Balancer contributors of a potential read-only reentrancy manipulation vector to getRate() (see public disclosure here).

In order to mitigate reentrancy risk, Sturdy contributors added a _validateOracle() function to key user-facing functions (deposit, withdraw, borrow, repay) in the LendingPool contract; the addition called the state-altering function WITHDRAW_INTERNAL for the Balancer pool, triggering Balancer’s reentrancy lock.

This solution was shared with the auditor as well as various Balancer contributors.

Exploit

While _validateOracle() was added to most of the user-facing functions, we failed to add it to setUserUseReserveAsCollateral(). This deprecated function enables users to toggle whether or not an asset is used as collateral (and therefore counted towards their loan health). This opened up a vulnerability that enabled the attacker to use the inflated Balancer LP price in order to set a different asset as not being collateral, which allowed them to withdraw it and left their position undercollateralized.

Below is a step-by-step walkthrough of the exploit (credit to BlockSec):

  1. Borrow 110k ETH via an Aave flashloan
  2. Deposit 1,000 ETH to Curve to get 1,000 Curve stETH (or any collateral asset other than Balancer wstETH/WETH)
  3. Deposit 109k ETH to Balancer to get 109k Balancer wstETH/WETH
  4. Deposit 1,000 Curve stETH and 233 Balancer wstETH/WETH as collateral to Sturdy
  5. Borrow 513 WETH from Sturdy
  6. Manipulate the price of Balancer wstETH/WETH via the read-only reentrancy vulnerability
  7. Set Curve stETH as non-collateral. Because there was no _validateOracle() function added here, the inflated Balancer wstETH/WETH price was used. As a result, the protocol considered the 233 Balancer wstETH/WETH to sufficiently collateralize the 513 WETH loan
  8. Withdraw 1,000 Curve stETH from Sturdy
  9. As the price of Balancer wstETH/WETH returns to normal, liquidate the position with 236 WETH to reclaim 233 Balancer wstETH/WETH. Steps 3–9 can be repeated until there is no WETH left in the pool
  10. Repay the flashloan

Aftermath

At 1:06 AM UTC on June 12th, an attacker exploited Sturdy’s Ether market and stole 442 ETH using the above method, resulting in a loss of 504 ETH to the protocol. The total size of the ETH lending pool is 2,109 ETH. No other assets or pools were impacted.

Shortly after being notified, Sturdy contributors rushed to pause the protocol; an extra $150k worth of ETH was saved thanks to the quick response.

Since then, we have been working tirelessly to track down the perpetrator in collaboration with a team of world-class security experts with a stellar track record of success. This investigation has successfully enabled us to obtain key information on the attacker, which has been shared with relevant parties. Despite our attempts to negotiate, the attacker has been unwilling to communicate thus far, indicating that recovery of funds will likely have to be performed by law enforcement.

In the meantime, we want to ensure that users can access their funds as soon as possible. We are currently gathering the requisite funds on-chain to fill the hole and reopen the Ether market.

We have also figured out a technical workaround to ensure that borrowers in the Ether market will not accrue interest in the interim. When the market unpauses, borrowers will have earned yield on their collateral without having to pay any interest, earning roughly 30% APY net depending on leverage amount. Lenders will have earned a portion of yield from collateral (around 1–2% APY) and $STRDY rewards.

Sturdy’s stablecoin market was unaffected and continues to function as always after a brief pause of the market out of an abundance of caution following the exploit.

Sturdy is committed to building a more robust, resilient protocol and regaining user trust brick by brick. This experience has only further increased our focus on security, and we look forward to making Sturdy v2 the safest lending protocol that DeFi has ever seen.

--

--

Sturdy

The first DeFi protocol for interest-free borrowing and high yield lending.