Asymmetry contest - ToonVH's results

A protocol to help diversify and decentralize liquid staking derivatives.

General Information

Platform: Code4rena

Start Date: 24/03/2023

Pot Size: $49,200 USDC

Total HM: 20

Participants: 246

Period: 6 days

Judge: Picodes

Total Solo HM: 1

Id: 226

League: ETH

Asymmetry Finance

Findings Distribution

Researcher Performance

Rank: 32/246

Findings: 4

Award: $209.86

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

3.4908 USDC - $3.49

Labels

bug
3 (High Risk)
low quality report
satisfactory
upgraded by judge
duplicate-1098

External Links

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L81 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L98

Vulnerability details

Impact

An attacker can frontrun the first deposit on the safETH contract. By exploiting a donation attack an attacker can set the price of safETH very high. Because of division rounding the user will then receive less safETH than normal. The attacker can then withdraw his safETH for all (or a portion) of funds in the contract. Thus stealing (a part of) the users deposit.

Proof of Concept

Assume only 1 derivative exists (wstETH): (1 wstETH = 1 ETH for simplicity)

  1. Attacker deposits 1 wei of ether -> receives 1 wei of safETH (preDepositPrice is initialized on 1e18)
  2. Attacker transfers 10.01 wstETH to WstETH derivative contract (donation).
  3. User deposits 10 ETH to safETH:
  4. underlyingValue is 1.001e19, totalSupply is 1 => preDepositPrice is 1.001e37
  5. mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice => 1e37/1.001e37 will equal 0 (division rounds to zero) -> user receives 0 safETH.
  6. Attacker can unstake his 1 wei of safETH to withdraw all 20.01 wstETH in the contract.

If multiple derivatives exist an attacker would have to deposit more than 1 wei in step 1 in order to receive a non-zero amount of safETH. This makes the attack less effective but still allows for stealing a part of the first deposit. e.g., Assume 3 derivatives with equal weights exist:

  1. Attacker deposits 3 wei of ether -> receives 3 wei of safETH (preDepositPrice is initialized on 1e18)
  2. Attacker transfers 10.02 wstETH to WstETH derivative contract (better rounding than 10.01).
  3. User deposits 10 ETH to safETH:
  4. underlyingValue is 1.002e19, totalSupply is 3 => preDepositPrice is 0.334e37
  5. mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice => 1e37/0.334e37 will equal 2 -> user receives 2 safETH.
  6. As a result 5 wei of safETH is backed by 20.01 ETH of derivatives. The attacker can withdraw his 3 wei of safETH to receive 12 ETH and thus steal part of the users deposit.

Tools Used

Manual review

Deposit some funds on initialization

#0 - c4-pre-sort

2023-04-03T07:21:27Z

0xSorryNotSorry marked the issue as low quality report

#1 - c4-pre-sort

2023-04-04T12:49:59Z

0xSorryNotSorry marked the issue as duplicate of #715

#2 - c4-judge

2023-04-21T14:58:42Z

Picodes marked the issue as satisfactory

#3 - c4-judge

2023-04-24T21:39:18Z

Picodes changed the severity to 3 (High Risk)

Findings Information

🌟 Selected for report: HollaDieWaldfee

Also found by: 0Kage, 0x52, 0xRobocop, Cryptor, HHK, MiloTruck, ToonVH, adriro, carrotsmuggler, d3e4, igingu

Labels

bug
3 (High Risk)
satisfactory
duplicate-210

Awards

195.1013 USDC - $195.10

External Links

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L108 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L118

Vulnerability details

Impact

The withdraw function of the reth derivative contract assumes it can always burn rETH for ETH. This is not the case. If Rocket Pool's deposit pool is empty, rETH.burn() will revert. As a result the derivative's withdraw() will revert (See note in RP docs). Subsequently SafETH.unstake() will also revert. This means that no user will be able to unstake their SafETH as long as Rocket Pool's deposit pool is empty (as seen here this can be an extended period of time (e.g., 5 months).

(note: rebalanceToWeights() will also fail)

Proof of Concept

  1. User deposits 50 ETH into SafETH
  2. Assume 20 ETH (of the 50) is swapped into rETH
  3. Rocket Pool uses the 20 ETH to create a minipool.
  4. Rocket Pool's deposit pool now only has 4 ETH
  5. User attempts to unstake his SafETH
  6. We attempt to burn 20ETH worth of rETH -> reverts since only 4 ETH is available
  • Sell rETH on a DEX if rETH is unable to be burned.

#0 - c4-pre-sort

2023-04-04T19:54:46Z

0xSorryNotSorry marked the issue as duplicate of #210

#1 - c4-judge

2023-04-21T16:35:35Z

Picodes marked the issue as satisfactory

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L212

Vulnerability details

Impact

An attacker can manipulate ethPerDerivative() for the rETH-derivative, allowing him to mint cheap SafETH which can then be unstaked at normal prices. If repeated, could drain a large amount of funds.

Mechanisms

  • ethPerDerivative() for rETH changes depending on _amount: either returns the poolPrice or the native rETH price.

  • An attacker can manipulate the poolPrice to significantly discount rETH (e.g., using flashloan)

Proof of Concept

Assumptions:

  • rETH is the only derivative used (for math simplicity)
  • rETH native price is 1 ETH (for math simplicity)
  • SafETH holds 5000 rETH (worth 5000 ETH)

Exploit:

  1. Attacker sells large amount of rETH on uniswap (e.g., using flashloan) -> rETH price is discounted to 0.5ETH
  2. Attacker stakes 100 ETH on SafETH
  3. ethPerDerivative on line 73 in stake() returns the poolPrice. This is because SafETH holds 5000 rETH, which is larger than Rocket Pool's deposit pool limit, thus poolCanDeposit() will be false.
  4. As a result underlyingValue will only be half of the actual (native) value.
  5. ethPerDerivative on line 92 in stake() will return the native value.
  6. mintAmount will be double of what it should be. -> Attacker minted SafETH at half the price
  7. Attacker rebuys large amount of rETH on uniswap
  8. Attacker unstakes SafETH at normal prices
  9. Repeat

ethPerDerivative() should return the same value regardless of amount. Either the market price or the native price.

#0 - c4-pre-sort

2023-04-04T16:04:31Z

0xSorryNotSorry marked the issue as duplicate of #1035

#1 - toshiSat

2023-04-06T21:01:52Z

This is not a duplicate of #1035

#2 - c4-sponsor

2023-04-06T21:29:56Z

toshiSat marked the issue as sponsor confirmed

#3 - elmutt

2023-04-06T21:30:54Z

We believe this is potentially a real issue and not a duplicate of 1035

#4 - c4-sponsor

2023-04-07T00:37:10Z

elmutt marked the issue as sponsor disputed

#5 - c4-sponsor

2023-04-07T00:37:20Z

elmutt marked the issue as sponsor confirmed

#6 - c4-judge

2023-04-21T13:56:02Z

Picodes marked the issue as satisfactory

Awards

11.1318 USDC - $11.13

Labels

bug
2 (Med Risk)
low quality report
satisfactory
duplicate-363

External Links

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L87

Vulnerability details

Impact

If no derivatives are active (this can be because none have been added yet OR because all weights are 0), a user calling stake() will lose all ETH that was sent.

Explanation

If no derivatives have been set up yet the for loop on line 84 will be skipped. If derivatives have been set up but all weights equal zero the if-statement on line 87 will skip all iterations of this loop. In both cases, this will result in totalStakeValueEth being 0. Subsequently, mintAmount will be 0 which means the user will not receive any safETH. As a result the user can not 'unstake' their ETH that was sent to the contract.

Tools Used

Manual review

revert if mintAmount == 0.

#0 - c4-pre-sort

2023-04-03T07:19:48Z

0xSorryNotSorry marked the issue as low quality report

#1 - c4-pre-sort

2023-04-04T19:19:13Z

0xSorryNotSorry marked the issue as duplicate of #363

#2 - c4-judge

2023-04-21T16:31:42Z

Picodes marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter