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: 79/126
Findings: 1
Award: $0.01
🌟 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/main/src/managers/LockManager.sol#L274-L294
An attacker can lock his/her funds for a victim player using the lockOnBehalf() method.
However, this action triggers the _lock() method
which reset the lockedToken.unlockTime
.
Because there is no minimum quantity to lock, an attacker can reset the lockedToken.unlockTime
of a victim indefinitely. This is a DoS attack on the unlock
functionality of the victim.
The lockOnBehalf() method allows to lock own funds for another address:
LockManager.sol#L274-L294 274 /// @inheritdoc ILockManager 275 function lockOnBehalf( 276 address _tokenContract, 277 uint256 _quantity, 278 address _onBehalfOf 279 ) 280 external 281 payable 282 notPaused 283 onlyActiveToken(_tokenContract) 284 onlyConfiguredToken(_tokenContract) 285 nonReentrant 286 { 287 address tokenOwner = msg.sender; 288 address lockRecipient = msg.sender; 289 if (_onBehalfOf != address(0)) { 290 lockRecipient = _onBehalfOf; 291 } 292 293 _lock(_tokenContract, _quantity, tokenOwner, lockRecipient); 294 }
This method resets the lockedToken.unlockTime
of the lockRecipient
address. Even if the attacker locks 1 wei on behalf of lockRecipient
, the lockedToken.unlockTime
is set again at least to lockdrop.minLockDuration
:
LockManager.sol#L382-L384 382 lockedToken.unlockTime = 383 uint32(block.timestamp) + 384 uint32(_lockDuration);
So, an attacker can postpone the unlockTime of a victim indefinitely, because the victim will not be able to call the unlock method
successfully (line 410):
LockManager.sol#L400-L411 400 /// @inheritdoc ILockManager 401 function unlock( 402 address _tokenContract, 403 uint256 _quantity 404 ) external notPaused nonReentrant { 405 LockedToken storage lockedToken = lockedTokens[msg.sender][ 406 _tokenContract 407 ]; 408 if (lockedToken.quantity < _quantity) 409 revert InsufficientLockAmountError(); 410 if (lockedToken.unlockTime > uint32(block.timestamp)) 411 revert TokenStillLockedError();
Visual inspection
Only a pre-authorized address should be able to lock funds on behalf of another address. So, we suggest to introduce a mapping to track pre-authorized addresses and add/remove methods to manage them.
DoS
#0 - c4-judge
2024-06-05T12:57:58Z
alex-ppg marked the issue as satisfactory