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: 32/126
Findings: 3
Award: $0.02
🌟 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.0042 USDC - $0.00
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L275-L294 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L311-L399
A malicious user can call the lockOnBehalf function with a valid _onBehalfOf address and a _quantity of zero, effectively extending the lock duration of the _onBehalfOf address without actually locking any tokens.
The lockOnBehalf function allows a user to lock tokens on behalf of another address (_onBehalfOf). However, the function does not check if the _quantity parameter is greater than zero.
This allows a malicious user to pass a valid _onBehalfOf address and a _quantity of zero, effectively extending the lock duration of the _onBehalfOf address without actually locking any tokens.
Manual review
In the lockOnBehalf function, add a check to ensure that the _quantity parameter is greater than zero before calling the _lock function. This will prevent the lock duration from being extended without actually locking any tokens.
if (_quantity == 0) revert ZeroQuantityError();
Context
#0 - c4-judge
2024-06-05T12:59:00Z
alex-ppg marked the issue as partial-75
🌟 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.0105 USDC - $0.01
unlockTime can be shortened when lock duration is extended
The issue lies in the calculation of the new unlockTime for the locked tokens.
`` uint32 lastLockTime = lockedTokens[msg.sender][tokenContract] .lastLockTime; lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration); }
The code assumes that the lastLockTime stored in the lockedTokens mapping represents the time when the tokens were last locked. However, it does not consider the possibility that the user may have set a different lock duration in the past, which could have already extended the unlockTime.
In this case, simply adding the new _duration to the lastLockTime would result in an incorrect unlockTime.
Suppose a user, Alice, has locked some tokens for a duration of 30 days (2,592,000 seconds) on January 1st, 2023, at 12:00:00 UTC. Initially, the lockedTokens mapping would be:
lockedTokens[Alice][tokenContract] = { quantity: 1000, lastLockTime: 1672531200, // January 1st, 2023, 12:00:00 UTC unlockTime: 1675123200, // January 31st, 2023, 12:00:00 UTC (lastLockTime + 30 days) remainder: 0 }
Now, let's say on January 15th, 2023, at 12:00:00 UTC, Alice wants to extend the lock duration to 45 days (3,888,000 seconds). According to the current implementation, the code would calculate the new unlockTime as follows:
uint32 lastLockTime = lockedTokens[msg.sender][tokenContract].lastLockTime; // 1672531200 lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration); // 1672531200 + 3888000 = 1676419200
The new unlockTime would be set to 1676419200, which corresponds to February 14th, 2023, 12:00:00 UTC.
However, this calculation is incorrect because it does not consider the original unlockTime of January 31st, 2023, 12:00:00 UTC. The correct unlockTime should be calculated based on the current block.timestamp (January 15th, 2023, 12:00:00 UTC) plus the new lock duration of 45 days.
The correct unlockTime should be 1677654000, which corresponds to March 1st, 2023, 12:00:00 UTC, as expected.
Manual review
The new unlockTime should be calculated based on the current block.timestamp and the new _duration.
lockedTokens[msg.sender][tokenContract].unlockTime = uint32(block.timestamp) + uint32(_duration);
Context
#0 - c4-judge
2024-06-05T12:52:14Z
alex-ppg marked the issue as partial-75
🌟 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
It's possible for an authorized caller to call disapproveUSDPrice and then call approveUSDPrice. This means an authorized caller can have disapproval and approval counts at the same time.
This can even lead to a situation where usd price can't be updated. For instance, if the number of authorized callers is 5. One user approves and disapproves a usd price, the four other callers approve and disapprove equally (2 for, 2 against).
With the initial user having an "approve" and "disapprove", "approve" would be 3 likewise "disapprove".
An authorized caller can call disapproveUSDPrice funtion first. Then, call approveUSDPrice function after.
Unlike in disapproveUSDPrice function, there's nothing stopping an authorized caller, who has called disappproveUSDPrice function initially, from calling approveUSDPrice function.
In essence, an authorized caller can have his "vote" count for approve usd price and disapprove usd price.
Manual review
In approveUSDPrice function, also check if msg.seder has already called disapproveUSDPrice function.
Access Control
#0 - c4-judge
2024-06-05T12:42:43Z
alex-ppg marked the issue as satisfactory