Platform: Code4rena
Start Date: 13/05/2022
Pot Size: $30,000 USDC
Total HM: 8
Participants: 65
Period: 3 days
Judge: hickuphh3
Total Solo HM: 1
Id: 125
League: ETH
Rank: 12/65
Findings: 4
Award: $460.58
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: pedroais
Also found by: 0x4non, 0x52, 0xf15ers, 0xliumin, CertoraInc, Dravee, GimelSec, IllIllI, MaratCerby, StErMi, TerrierLover, WatchPug, berndartmueller, cccz, dipp, fatherOfBlocks, hake, hickuphh3, hyh, isamjay, mtz, oyc_109, p4st13r4, peritoflores, rotcivegaf, saian, simon135, sorrynotsorry, sseefried, tabish, z3s
14.8433 USDC - $14.84
Judge has assessed an item in Issue #63 as High risk. The relevant finding follows:
#0 - HickupHH3
2022-06-06T04:05:26Z
ETH can accidentally be sent to a contract that rejects the transfer because the sent value returned by address(_to).call{value: receivedETHAmount}(''); is not actually checked. https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/LidoVault.sol#L140-L142 On line 140 a call is made to transfer ETH (bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); Then on line 141 receivedETHAmount is returned. And on line 142 the sending of ETH is checked.
Recommended Mitigation Steps To fix this, line 141 and line 142 should be swapped.
#1 - HickupHH3
2022-06-06T04:06:24Z
Duplicate of #157
367.5749 USDC - $367.57
https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/YieldManager.sol#L129-L134 https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/YieldManager.sol#L160-L161
An attacker can use MEV (via gas auction or Flashbots or control of miners) to cause an unfair division of yield. By providing a very large (relative to the size of all other stablecoin deposits combined) stablecoin deposit Just-in-Time before an admin's call to distributeYield the stablecoin deposited by the attacker will receive a very large amount of the yield and the attacker can immediately withdraw their deposit after yield is distributed. We assume this allows an attacker to get a lot of the yield reward even though they haven't provided any deposit that has been borrowed. However, the exact mechanism for how yield is distributed to lenders of a particular stablecoin is in LendingPool.sol, which is out of scope. However it is implied in the documentation of this repo that it is based on the balance of that asset the lender has provided. We have confirmed that in LendingPool.sol the yield is distributed based on the proportion of the asset provided. However, even ignoring this, MEV can still be used to unfairly hurt lenders of other stablecoins.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The attacker has thus made the asset they deposited (and thus themselves) receive much of the yield even though they provide no value to Sturdy since none of their deposit is ever borrowed so the never do anything to earn yield for sturdy. This attack can be done by a whale or by borrowing (even from sturdy) assets and converting them to a stablecoins accepted by sturdy before i. and returning them after iii. This will essentially be cost free for the attacker, none of their capital will ever be tied up by borrowers.
The simplest way to mitigate this is for the admin to use flashbots or some other means of submitting the distributeYield call that skips the mempool. This is only a partial mitigation since attackers can still withdraw right after yield is distributed and get lucky by depositing soon before the distribution thus still capture more yield than they should have. A better mitigation could use something like snapshotting who has deposited since the last yield distribution and only give these depositers yield based on the size of their deposits the next time yield is distributed.
#0 - sforman2000
2022-05-18T04:12:45Z
We will use flashbots and vary when/how often yield is harvested to mitigate this.
#1 - HickupHH3
2022-06-03T07:38:13Z
I take reference to discussions on Discord and in a thread below: https://github.com/code-423n4/2022-03-biconomy-findings/issues/135
To quote from 0xleastwood: "Protocol leaked value in has a broad context but I think most judges can agree that it would pertain to rewards being paid out a lower rate than expected. Or, users can extract small amounts (up to debate on what is considered to be small) from the protocol under certain assumptions."
Hence, as per the TLDR risk assessment:
2 — Med: Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.
I would downgrade this to a medium severity.
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, 0xliumin, AlleyCat, BouSalman, Dravee, Funen, GimelSec, Hawkeye, MaratCerby, Picodes, StErMi, TerrierLover, WatchPug, Waze, berndartmueller, bobirichman, cryptphi, csanuragjain, defsec, delfin454000, dipp, fatherOfBlocks, hake, hickuphh3, hyh, joestakey, kebabsec, mics, mtz, oyc_109, p4st13r4, p_crypt0, robee, rotcivegaf, sikorico, simon135, sorrynotsorry, tintin
54.7123 USDC - $54.71
sent
value returned by address(_to).call{value: receivedETHAmount}('');
is not actually checked.https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/LidoVault.sol#L140-L142
On line 140 a call is made to transfer ETH (bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}('');
Then on line 141 receivedETHAmount
is returned.
And on line 142 the sending of ETH is checked.
To fix this, line 141 and line 142 should be swapped.
https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/LidoVault.sol#L122-L142
Neither _withdrawFromYieldPool
nor withdrawCollateral
validates that _to
is not the 0x0 address. They should. Unlike token transfers which typically reject transfers to the 0x0 address, call
on the 0x0 address will not fail if the caller has the amount of ETH they send (note this is different from the last issue reported in the QA report, which is regarding contracts only).
https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/GeneralVault.sol#L51 It is not 20%, it is limited to less than or equal to 30%
https://github.com/code-423n4/2022-05-sturdy/blob/main/smart-contracts/YieldManager.sol#L217
@param _amount The mount of asset
should read @param _amount The amount of asset
. amount
is spelled incorrectly.
A malicious admin can call setTreasuryInfo
immediately before calling processYield
to increase the vault fee and then set the vault fee back to the advertised value by calling setTreasuryInfo
immediately after.
Instead setTreasuryInfo should only be allowed to set the vault fee once. After this, the vault fee should be set when calling processYield
for the next time processYield is called.
#0 - HickupHH3
2022-06-06T04:08:51Z
Low issues: Vault fee comment, deceptively increase fees NC issues: zero address checks, spelling Upgraded: first issue.
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, 0xliumin, Cityscape, Dravee, Fitraldys, Funen, GimelSec, Hawkeye, JC, MaratCerby, SooYa, StErMi, Tomio, WatchPug, Waze, bobirichman, defsec, delfin454000, fatherOfBlocks, hake, hansfriese, hickuphh3, ignacio, joestakey, kebabsec, mics, mtz, oyc_109, robee, rotcivegaf, samruna, sikorico, simon135, z3s
23.4569 USDC - $23.46
https://github.com/code-423n4/2022-05-sturdy/blob/78f51a7a74ebe8adfd055bdbaedfddc05632566f/smart-contracts/ConvexCurveLPVault.sol#L71
The call to balanceOf
on the next line will fail anyway.