Platform: Code4rena
Start Date: 31/01/2023
Pot Size: $90,500 USDC
Total HM: 47
Participants: 169
Period: 7 days
Judge: LSDan
Total Solo HM: 9
Id: 211
League: ETH
Rank: 169/169
Findings: 1
Award: $1.15
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xdeadbeef0x
Also found by: 0Kage, 0xNazgul, 0xRobocop, Aymen0909, KIntern_NA, Kenshin, KingNFT, Krace, Kumpa, SadBase, aashar, apvlki, btk, cccz, critical-or-high, eccentricexit, fs0c, gjaldon, hansfriese, immeas, mert_eren, mgf15, mrpathfindr, orion, peanuts, rvi0x, rvierdiiev, supernova, ulqiorra, waldenyan20, y1cunhui
1.1529 USDC - $1.15
##Description
One of the major dangers of calling external contracts is that they can take over the control flow. In the reentrancy attack (a.k.a. recursive call attack), a malicious contract calls back into the calling contract before the first invocation of the function is finished. This may cause the different invocations of the function to interact in undesirable way
user can claimRewards more than one time !
function claimRewards(address user, IERC20[] memory _rewardTokens) external accrueRewards(msg.sender, user) { for (uint8 i; i < _rewardTokens.length; i++) { uint256 rewardAmount = accruedRewards[user][_rewardTokens[i]]; if (rewardAmount == 0) revert ZeroRewards(_rewardTokens[i]); EscrowInfo memory escrowInfo = escrowInfos[_rewardTokens[i]]; if (escrowInfo.escrowPercentage > 0) { _lockToken(user, _rewardTokens[i], rewardAmount, escrowInfo); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, true); } else { _rewardTokens[i].transfer(user, rewardAmount); // <- Reentrancy emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, false); } accruedRewards[user][_rewardTokens[i]] = 0; } }
when the user calm Reward the if condition here will pass as false
if (rewardAmount == 0) revert ZeroRewards(_rewardTokens[i]);
and the 2nd one
if (escrowInfo.escrowPercentage > 0) { _lockToken(user, _rewardTokens[i], rewardAmount, escrowInfo); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, true); }
the bug is right here
_rewardTokens[i].transfer(user, rewardAmount); // <- here
manually test
The best practices to avoid Reentrancy weaknesses are:
* Make sure all internal state changes are performed before the call is executed. This is known as the Checks-Effects-Interactions pattern * Use a reentrancy lock (ie. OpenZeppelin's ReentrancyGuard.
#0 - c4-judge
2023-02-16T07:38:14Z
dmvt marked the issue as duplicate of #54
#1 - c4-sponsor
2023-02-18T12:10:45Z
RedVeil marked the issue as sponsor confirmed
#2 - c4-judge
2023-02-23T01:00:33Z
dmvt marked the issue as partial-25
#3 - c4-judge
2023-02-23T01:11:22Z
dmvt changed the severity to 3 (High Risk)