Platform: Code4rena
Start Date: 28/09/2023
Pot Size: $36,500 USDC
Total HM: 5
Participants: 115
Period: 6 days
Judge: 0xDjango
Total Solo HM: 1
Id: 290
League: ETH
Rank: 104/115
Findings: 1
Award: $4.37
π Selected for report: 0
π Solo Findings: 0
π Selected for report: Bauchibred
Also found by: 0x3b, 0xDetermination, 0xMosh, 0xScourgedev, 0xTheC0der, 0xTiwa, 0xWaitress, 0xdice91, 0xfusion, 0xpiken, 0xprinc, 0xweb3boy, ArmedGoose, Aymen0909, Breeje, Brenzee, Daniel526, DavidGiladi, DeFiHackLabs, Flora, Fulum, HChang26, Hama, IceBear, J4X, Krace, KrisApostolov, Maroutis, Mirror, MohammedRizwan, Norah, PwnStars, SPYBOY, TangYuanShen, Testerbot, ThreeSigma, Tricko, al88nsk, alexweb3, ast3ros, berlin-101, bin2chen, blutorque, btk, d3e4, deth, e0d1n, ether_sky, ge6a, gkrastenov, glcanvas, hals, imare, inzinko, jkoppel, jnforja, joaovwfreire, josephdara, kutugu, lotux, lsaudit, mahdirostami, merlin, n1punp, nadin, neumo, nisedo, nobody2018, oakcobalt, orion, peanuts, pep7siup, pina, ptsanev, rokinot, rvierdiiev, said, santipu_, sashik_eth, seerether, squeaky_cactus, terrancrypt, tonisives, twicek, vagrant, xAriextz, y4y
4.3669 USDC - $4.37
AccrueTokens internally may revert due to underflow in some cases. This function is called in Prime.sol, which will cause some important processes to fail. Venus is a lending protocol that is very time-sensitive. Any temporary DOS will have a negative impact on all participants of Venus.
Letβs first take a look at the important processes mentioned above:
XVSVault.deposit/requestWithdrawal Prime.xvsUpdated _accrueInterestAndUpdateScore _executeBoost accrueInterest PrimeLiquidityProvider.accrueTokens //revert
//in Comptroller PolicyFacet.mintVerify/redeemVerify/borrowVerify/repayBorrowVerify/liquidateBorrowVerify/seizeVerify/transferVerify Prime.accrueInterestAndUpdateScore _executeBoost accrueInterest PrimeLiquidityProvider.accrueTokens //revert
From the above processes, we can see that if accumulateTokens
revert, the impact will be significant.
File: contracts\Tokens\Prime\PrimeLiquidityProvider.sol 249: function accrueTokens(address token_) public { ...... 254: uint256 blockNumber = getBlockNumber(); 255: uint256 deltaBlocks = blockNumber - lastAccruedBlock[token_]; 256: 257: if (deltaBlocks > 0) { 258: uint256 distributionSpeed = tokenDistributionSpeeds[token_]; 259: uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this)); 260: 261:-> uint256 balanceDiff = balance - tokenAmountAccrued[token_]; 262: if (distributionSpeed > 0 && balanceDiff > 0) { 263: uint256 accruedSinceUpdate = deltaBlocks * distributionSpeed; 264: uint256 tokenAccrued = (balanceDiff <= accruedSinceUpdate ? balanceDiff : accruedSinceUpdate); 265: 266: tokenAmountAccrued[token_] += tokenAccrued; 267: emit TokensAccrued(token_, tokenAccrued); 268: } 269: 270: lastAccruedBlock[token_] = blockNumber; 271: } 272: }
Under normal circumstances, balance
is definitely greater than or equal to tokenAmountAccrued[token_]
which is no problem.
We noticed that the sweepToken function is used to sweep accidental ERC-20 transfers to this contract. This is to prevent users from accidentally transferring ERC20 to this contract, and the owner can return the token to the user. However, malicious users can take advantage of this to transfer the accumulated tokens of the contract and request the owner to return the tokens.
sweepToken
, accumulateTokens
has been called, so the value of tokenAmountAccrued[token]
already includes the token transferred by the malicious user.sweepToken
is called, the token held by this contract must be less than tokenAmountAccrued[token]
. This causes subsequent accumulateTokens
revert at line L261.Consider the following scenario:
For simplicity, we assume there is only one token: tokenA.
tokenA.balanceOf(address(this)) = 1000e18 tokenAmountAccrued[tokenA] = 1000e18
tokenA.balanceOf(address(this)) = 1000e18 + 10e18 = 1010e18
.accumulateTokens
, the malicious user calls accumulateTokens(tokenA)
. Therefore tokenAmountAccrued[tokenA] = 1010e18
. Note: If accumulateTokens
has been triggered by an external process, there is no need to call it specifically.sweepToken(tokenA, userAddr, 10e18)
.tokenA.balanceOf(address(this)) = 1000e18
. tokenAmountAccrued[tokenA] = 1010e18
. Then, subsequent accumulateTokens
called by external processes will revert due to underflow.This issue discusses the owner's return of tokens that were mistakenly transferred by users, and is not a malicious call by the owner.
Manual Review
Adding check in sweepToken
: whether the balance in the contract will be less than tokenAmountAccrued[token_]
after transfer out.
DoS
#0 - c4-pre-sort
2023-10-05T01:27:00Z
0xRobocop marked the issue as duplicate of #42
#1 - c4-judge
2023-10-31T17:09:53Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-11-03T01:57:55Z
fatherGoose1 marked the issue as grade-b