Lack of timelock on rigidRedemption, enables to steal yield from other users #290
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
downgraded by judge
Judge downgraded the risk level of this issue
edited-by-warden
M-15
primary issue
Highest quality submission among a set of duplicates
satisfactory
satisfies C4 submission criteria; eligible for awards
selected for report
This submission will be included/highlighted in the audit report
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L232
Vulnerability details
Impact
The withdraw function of the LybraEUSDVaultBase vaults, uses a time softlock to prevent users from hopping in and out of the protocol, to gain access to the yield generated by other users and then leave right away (by charging a small percentage from the withdrawn amount).
The same measure isn't applied to rigidRedemptions, which enable a user to withdraw most of the underlying assets at any time after deposit.
This enables an user to deposit into the pool right before a rabase is about to happen, get access to the yield generated by other users and leave by calling rigidRedemption and withdraw on the tokens left by rigid redemption (the amount charged on the leftovers assets, can be outbalanced by the yield).
Therefor a malicious user to get access to yield that they didn't generated, effectively stealing it from others. The amount that the user will get access to will vary based on the deposited amounts.
Proof of Concept
This issue involves 3 function:
withdraw(address onBehalfOf, uint256 amount)
from theLybraEUSDVaultBase
contract (https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L98) which internally callscheckWithdrawal(address user, uint256 amount)
(https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L98) to check that 3 days has passed after deposit, and charges the user otherways:rigidRedemption(address provider, uint256 eusdAmount)
from theLybraEUSDVaultBase
contract (https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L232) which enables a user to withdraw the full borrowed amount getting back a 1:1 ratio of collateral (the rest will be left in the vault, and can be withdrawn)excessIncomeDistribution(uint256 stETHAmount)
from theLybraStETHDepositVault
contract (https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/pools/LybraStETHVault.sol#L62) which enables anyone to buy the stETH, generated by lido to the vault (or by charging on withdraws and rigidRedemptions), for EUSD, allocating them to EUSD holders through rebasingScenario:
Step0: users use the protocol as intended depositing stETH which will generate a yield
Step1: Bob calls the rebase mechanism (excessIncomeDistribution)
Step2: Alice sees the rebase and preceeds it with a deposit (either by frontruinng or by pure prediction, since stETH rebase happens daily at a fixed time)
Step3: Right after Bob's rebase gets executed, Alice calls rigidRedemption (to repay the full debt) followed by withdraw (to get the difference out), getting most of the stETH back and some EUSD
Step4: Since the stETH charged by the withdraw function is left in the vault, if she wants, Alice can now call excessIncomeDistribution, to get the tokens back, using the EUSD recived by rebasing, and leaving with slightly more stETH and some EUSD, that she got for free, leaving 0 debts and 0 assets deposited, having left her tokens in the vault for a few seconds.
Here is an hardhat script that shows the scenario above in javascript (each step is highlighted in the comments, and it will print all the balances to the console).
Before running it you'll have to install the
'@openzeppelin/test-helpers'
package:It will log the following content to the console:
Recommended Mitigation Steps
The same time lock logic that is applied to the withdraw function could be applied to rigidRedemption, making this type of interactions unprofitable.
Assessed type
Timing
The text was updated successfully, but these errors were encountered: