Platform: Code4rena
Start Date: 22/05/2024
Pot Size: $20,000 USDC
Total HM: 6
Participants: 126
Period: 5 days
Judge: 0xsomeone
Total Solo HM: 1
Id: 379
League: ETH
Rank: 5/126
Findings: 4
Award: $823.83
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Circolors
Also found by: 0rpse, 0x175, 0xAadi, 0xHash, 0xMax1mus, 0xMosh, 0xblack_bird, 0xdice91, 0xfox, 0xhacksmithh, 0xloscar01, 0xrex, 4rdiii, Audinarey, AvantGard, Bigsam, DPS, Dots, Drynooo, Dudex_2004, Evo, Kaysoft, King_, Limbooo, MrPotatoMagic, PENGUN, Sabit, SovaSlava, SpicyMeatball, TheFabled, Utsav, Varun_05, Walter, adam-idarrha, araj, aslanbek, ayden, bctester, biakia, bigtone, brgltd, carrotsmuggler, cats, crypticdefense, dd0x7e8, dhank, fandonov, fyamf, grearlake, iamandreiski, ilchovski, jasonxiale, joaovwfreire, lanrebayode77, m4ttm, merlinboii, niser93, nnez, octeezy, oxchsyston, pamprikrumplikas, rouhsamad, tedox, trachev, turvy_fuzz, twcctop, yotov721, zhaojohnson
0.0056 USDC - $0.01
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L275 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L382 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L410
When a user lock
his tokens then his unlockTime
increases by _lockDuration
in LockManager::_lock()
function _lock( address _tokenContract, uint256 _quantity, address _tokenOwner, address _lockRecipient ) private { ... @> uint32 _lockDuration = playerSettings[_lockRecipient].lockDuration; @> if (_lockDuration == 0) { _lockDuration = lockdrop.minLockDuration; } ... @> lockedToken.unlockTime = uint32(block.timestamp) + uint32(_lockDuration); ... }
Now, any user can deposit on behalf
of other user by calling LockManager::lockOnBehalf()
which will again increase
his unlockTime
as we've seen above
A malicious user can take advantage
of this function to grief
honest user by frontrunning
their unlock()
, which will make their unlock revert
due to below check
function unlock( address _tokenContract, uint256 _quantity ) external notPaused nonReentrant { ... @> if (lockedToken.unlockTime > uint32(block.timestamp)) revert TokenStillLockedError(); ... }
Important:- This can be done with 0(zero) token as there is no check for min amount
Honest user will be DOS
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L410C8-L411C44 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L382C1-L384C35
Manual Review
Ensure min
lock amount or avoid
locking tokens onBehalf of other users
DoS
#0 - c4-judge
2024-06-05T12:58:01Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: SpicyMeatball
Also found by: 0rpse, 0xMosh, 0xblack_bird, 0xdice91, 0xhacksmithh, 0xleadwizard, 0xmystery, Audinarey, AvantGard, Bigsam, Dots, EPSec, Eeyore, Janio, Limbooo, LinKenji, Mahmud, MrPotatoMagic, Myd, Oxsadeeq, Sabit, SovaSlava, Stefanov, Tychai0s, Utsav, Varun_05, Walter, adam-idarrha, ahmedaghadi, araj, aslanbek, ayden, bigtone, c0pp3rscr3w3r, carrotsmuggler, crypticdefense, dhank, fyamf, gajiknownnothing, gavfu, itsabinashb, jasonxiale, joaovwfreire, ke1caM, leegh, merlinboii, mitko1111, n4nika, pfapostol, prapandey031, rouhsamad, sandy, snakeeaterr, stakog, steadyman, swizz, tedox, th3l1ghtd3m0n, trachev, turvy_fuzz, xyz, yashgoel72, zhaojohnson
0.014 USDC - $0.01
When a user set his lockDuration
using setLockDuration()
, it ensures that unlockTime
is not reduced
but the problem is instead of adding block.timestamp
to _duration
in unlockTime, lastLockTime
is added, which reduces
the unlockTime
function setLockDuration(uint256 _duration) external { ... uint32 lastLockTime = lockedTokens[msg.sender][tokenContract].lastLockTime; @> lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration); ... }
//How this works(for simplicity taking time is hrs)
lockDuration = 3hrs
, so block.timestamp + duration = 5PM + 3hrs = 8PM, ie check pass because 8PM > 7PM(unlockTime)unlockTime can be reduced. As a result, user can unlock early
Manual Review
Replace lastLockTime
with block.timestamp
Error
#0 - CloudEllie
2024-05-31T16:55:36Z
See sponsor comments on #236
#1 - c4-judge
2024-06-04T12:48:41Z
alex-ppg marked issue #7 as primary and marked this issue as a duplicate of 7
#2 - c4-judge
2024-06-05T12:53:59Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: robertodf99
Also found by: 0xAadi, 0xAkira, 0xdice91, 0xhacksmithh, 0xleadwizard, AgileJune, Bauchibred, Bbash, Beosin, Bigsam, Dots, EPSec, EaglesSecurity, Eeyore, Evo, John_Femi, Mahmud, MrPotatoMagic, RotiTelur, Rushkov_Boyan, Sabit, Sentryx, Stormreckson, Topmark, Tychai0s, Utsav, Walter, ZanyBonzy, ZdravkoHr, adam-idarrha, araj, aslanbek, avoloder, bigtone, brevis, brgltd, carrotsmuggler, crypticdefense, dd0x7e8, dhank, djanerch, falconhoof, iamandreiski, joaovwfreire, leegh, merlinboii, mitko1111, pamprikrumplikas, pfapostol, prapandey031, swizz, trachev, twcctop, typicalHuman, unique, xyz
0.0148 USDC - $0.01
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L210
Other priceFeeds can approve/disapprove
the price proposed by a priceFeed and to prevent
a priceFeed from voting twice
, there is a check in disapproveUSDPrice()
function disapproveUSDPrice( uint256 _price ) ... @> if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) revert ProposalAlreadyApprovedError(); @> if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId) revert ProposalAlreadyDisapprovedError(); ... }
In above code, there are 2 checks
which ensures priceFeed has neither approved or disapproved the price.
But the problem
is there is only 1 check
in approveUSDPrice()
, which only checks for priceFeed has approved
but not for disapprove
, allowing a priceFeed to disapprove(via disapproveUSDPrice) first and then approve(via approveUSDPrice) the price, giving an option
to approve & disapprove simultaneously
function approveUSDPrice( uint256 _price ) ... @> if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) revert ProposalAlreadyApprovedError(); ... }
PriceFeed can approve & disapprove simultaneously
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177C4-L207C6 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L210C4-L242C6
Manual Review
Add this line in approveUSDPrice()
+ if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId) + revert ProposalAlreadyDisapprovedError();
Error
#0 - c4-judge
2024-06-05T12:42:21Z
alex-ppg marked the issue as satisfactory
28.7985 USDC - $28.80
When a user locks
his assets then remainder
& quantity
is saved in user's lockedToken.remainder
and lockedToken.quantity
. Also remainder is added
to quantity
when user locks his assets next
time
function _lock( address _tokenContract, uint256 _quantity, address _tokenOwner, address _lockRecipient ) private { ... @> uint256 quantity = _quantity + lockedToken.remainder; uint256 remainder; ... @> lockedToken.remainder = remainder; @> lockedToken.quantity += _quantity; ... }
Now the problem is, when a user unlock()
his assets then only
quantity
is reduced, but not remainder
function unlock( address _tokenContract, uint256 _quantity ) external notPaused nonReentrant { ... lockedToken.quantity -= _quantity; ... }
As a result, when user will lock again
then the left remainder
will be utilized again
//How this works
// calculate number of nfts remainder = quantity % configuredToken.nftCost; numberNFTs = (quantity - remainder) / configuredToken.nftCost;
User will get NFT at much cheaper price, which is loss for protocol
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L401C1-L427C6 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L344 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L380
Manual Review
Subtract
the remainder in unlock()
Error
#0 - c4-judge
2024-06-05T13:04:17Z
alex-ppg marked the issue as satisfactory
#1 - c4-judge
2024-06-05T13:04:46Z
alex-ppg changed the severity to 2 (Med Risk)