Platform: Code4rena
Start Date: 08/05/2023
Pot Size: $90,500 USDC
Total HM: 17
Participants: 102
Period: 7 days
Judge: 0xean
Total Solo HM: 4
Id: 236
League: ETH
Rank: 8/102
Findings: 2
Award: $1,859.08
馃専 Selected for report: 0
馃殌 Solo Findings: 0
1807.3974 USDC - $1,807.40
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/MaxLoopsLimitHelper.sol#L39-L42 https://github.com/code-423n4/2023-05-venus/blob/main/contracts/MaxLoopsLimitHelper.sol#L5
According to the comment on line 5, the _ensureMaxLoop()
function is used to avoid DOS but a malicious user can abuse this by ensuring that his account cannot be liquidated.
Assume that maxLoopLimit
is 20 and there are more than 20 pools. A malicious user simply needs to deposit 1 token into each pool. When someone tries to call liquidateAccount()
with his address and an array of orders. If the liquidator submits more than 20 orders, line 667 will fail. If the user tries to submit less than 20 orders, the loop on line 690-693 will fail.
Remove this check because there are only 2 public functions that call _ensureMaxLoop
internally. enterMarkets()
doesn鈥檛 have any side effects while the other function where _ensureMaxLoop
can be abused is in the liquidateAccount
function. The other functions that call _ensureMaxLoop
are all gated by access control.
Loop
#0 - c4-judge
2023-05-18T11:44:42Z
0xean marked the issue as duplicate of #327
#1 - 0xean
2023-05-30T22:34:31Z
This issue is invalid, the max loop limit constrains the number of pools.
#2 - c4-judge
2023-05-30T22:34:36Z
0xean marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2023-06-05T13:50:38Z
0xean marked the issue as satisfactory
51.6843 USDC - $51.68
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol#L1476-L1478
By minting 1 wei of token (actualMintAmount
is initialExchangeRateMantissa
unscaled by 1e18) and transferring the underlying directly to the vToken contract, it will inflate the way exchangeRate is calculated. This has the same underlying issue as the following:
function _exchangeRateStored() internal view virtual returns (uint256) { uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply */ uint256 totalCash = _getCashPrior(); uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves; uint256 exchangeRate = (cashPlusBorrowsMinusReserves * expScale) / _totalSupply; return exchangeRate; } }
Since 1 wei is minted, totalSupply
is no longer 0 therefore it will calculate the exchangeRate
. totalCash()
returns the current balance of the underlying tokens in the vToken contract (which is inflated due to malicious user transferring tokens directly to the vToken contract).
cashPlusBorrowsMinusReserves
is calculated as inflated value + 0 + 0 - 0 i.e. inflated value.
exchangeRate
is calculated as inflated value * 1e18 / 1 i.e. inflated value.
Therefore, all subsequent mints will need to mint at a much higher rate than the first minter. If subsequent mints are not large enough, it will only inflate the exchangeRate further.
After line 776, check that mintTokens cannot be 0.
DoS
#0 - c4-judge
2023-05-17T12:01:48Z
0xean marked the issue as duplicate of #314
#1 - c4-judge
2023-06-05T14:08:29Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-06-05T14:37:43Z
0xean changed the severity to 2 (Med Risk)