Popcorn contest - mrpathfindr's results

A multi-chain regenerative yield-optimizing protocol.

General Information

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

Popcorn

Findings Distribution

Researcher Performance

Rank: 102/169

Findings: 2

Award: $40.09

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

4.6115 USDC - $4.61

Labels

bug
3 (High Risk)
satisfactory
sponsor confirmed
duplicate-402

External Links

Lines of code

https://github.com/code-423n4/2023-01-popcorn/blob/d95fc31449c260901811196d617366d6352258cd/src/utils/MultiRewardStaking.sol#L170

Vulnerability details

Impact

Detailed description of the impact of this finding.

The state variable uint256 rewardAmount is equal to accruedRewards[user][_rewardTokens[i]];

The claimReward function requires the reward amount to NOT be 0 in order to execute the remainder of the function.

accruedRewards[user][_rewardTokens[i]] is called (and updated) after the transfer function _rewardTokens[i].transfer(user, rewardAmount); is called.

If a malicious contracts calls claimRewards, then funds can be transferd multiple times before the state variable accruedRewards[user][_rewardTokens[i]] is updated to 0

Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

https://github.com/code-423n4/2023-01-popcorn/blob/d95fc31449c260901811196d617366d6352258cd/src/utils/MultiRewardStaking.sol#L170

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); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, false); } accruedRewards[user][_rewardTokens[i]] = 0; } }

Tools Used

Manual Search

The claimRewards function has been modified to update the state before _lockToken(user, _rewardTokens[i], rewardAmount, escrowInfo);. and _rewardTokens[i].transfer(user, rewardAmount); are called.

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 { accruedRewards[user][_rewardTokens[i]] = 0; _rewardTokens[i].transfer(user, rewardAmount); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, false); } } }

#0 - c4-judge

2023-02-16T07:39:38Z

dmvt marked the issue as duplicate of #54

#1 - c4-sponsor

2023-02-18T12:11:06Z

RedVeil marked the issue as sponsor confirmed

#2 - c4-judge

2023-02-23T00:49:55Z

dmvt marked the issue as partial-50

#3 - c4-judge

2023-03-01T00:36:05Z

dmvt marked the issue as full credit

#4 - c4-judge

2023-03-01T00:37:39Z

dmvt marked the issue as satisfactory

#0 - c4-judge

2023-02-28T15:07:40Z

dmvt marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter