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: 40/126
Findings: 2
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.0056 USDC - $0.01
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L274-L294 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L382-L384 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L256-L261
Users are able to push unlockTime
of other users' locks further by locking dust amounts of tokens using lockOnBehalf
.
The function lockOnBehalf lets users lock tokens on other's behalf. This will increase the unlockTime
of the token locked:
lockedToken.unlockTime = uint32(block.timestamp) + uint32(_lockDuration);
As there are no minimum amount checks, an attacker could use dust amounts to easily affect unlockTime
. At this point the only way out for the funds would be setting the lock duration to a very small value, this is because if it is set to zero funds will be locked for another 30 days(minLockDuration), also note setLockDuration
will not let a user change his/her duration if it is decreasing the unlockTime
:
if ( uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); }
Example:
User locks some tokens for 30 days at time t
.
At the end of lock or a day before attacker locks on user's behalf.
Now the user will have to wait for an extra 30 days to withdraw these funds.
In order to prevent this a user would have to repeatedly call setLockDuration
to decrease its lock duration at many intervals. Otherwise attacker can keep locking to push unlockTime
further.
Manual review
You could introduce permission functionality for locking on other's behalf and/or add minimum amount checks for lockOnBehalf
so that the attack would be expensive.
DoS
#0 - c4-judge
2024-06-05T12:58:06Z
alex-ppg marked the issue as satisfactory
#1 - c4-judge
2024-06-05T13:00:01Z
alex-ppg changed the severity to 3 (High Risk)
🌟 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
A user can reduce a lockup time that was previously set, breaking one of the invariants of the protocol. This could for example be used to harvest bonus undeserved schnibbles.
Proof of Concept
The function setLockDuration
resets the lock duration for every token. While doing this, it uses the newly set duration plus lastLockTime
, which is the last point in time when a token was locked.
The issue arises because setLockDuration
does not update the token's lastLockTime
. As a result, after the initial locking, a user can set setLockDuration
to a bigger value, harvest rewards for a longer lock, and then use setLockDuration
again with the stale lastLockTime
to reduce the unlock time and receive undeserved rewards.
Example:
A user locks some tokens at time t
with a lock duration of 30 days.
After 20 days, the user calls setLockDuration and sets it to 60 days.
At this point, unlockTime is correctly set to t + 60
, with the lock duration being 60 days.
Now, block.timestamp
= t + 20
and lastLockTime
= t
. This allows the user to set the lock duration to 40 days since the following check passes:
if ( uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); }
The unlockTime is then set with the stale lastLockTime:
uint32 lastLockTime = lockedTokens[msg.sender][tokenContract] .lastLockTime; lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration);
This sets unlockTime to t + 40
days, decreasing the lock time that was previously set to t + 60
.
Manual review
setLockDuration
should set lastLockTime
to block.timestamp
after setting unlockTime.
Other
#0 - c4-judge
2024-06-05T12:52:21Z
alex-ppg marked the issue as partial-75