Open c4-bot-2 opened 4 months ago
This issue was judges as unsatisfactory
because it is due to a user mistake and it was reported by bot M-2. However, in M-2 the bot reported the gas cost as root cause:
The use of the deprecated transfer() function for an address may make the transaction fail due to the 2300 gas stipend
The issue I reported is different because the transaction doesn't fail: it succeeds and transfers an amount of native tokens. However, when an user asks to unlock that funds, he/she would fail. This behavior is very unfair and it is not due to a user mistake. Why an user should not use a non-EOA address to lock his/her funds? There is not documentation that advise against this behavior. I think it is a standard behavior to use a non-EOA addresses.
So, we think that the following behavior is very unfair: a user chooses to use a non-EOA address to lock funds on Munchables. After at least 30 days, the user discovers his/her funds are unrecoverable.
I want to add a very important thing: in the future, no contracts will be able to unlock their funds on the Munchables protocol. Even other protocol's contracts will not be able to do that. And we think this will be a problem. Let's think, for example, to a vault that aims to collect funds in order to mint Munchables, for example. It will be able to lock funds, but not to unlock them.
Could you please evaluate this issue again? Thank in advance
Hey @niser93, the submission has been graded by the validators, and did not receive judge feedback. Smart contracts not having a receive
or fallback payable
hook to accept native funds are the ones responsible for interacting with the contract securely.
Lines of code
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L400-L427
Vulnerability details
Locking native tokens using a non-EOA account makes funds unrecoverable
Impact
The lock() method allows to lock funds and mint Munchables NFT. The lockOnBehalf() method allows to lock funds on behalf of a player.
If a player uses a smart contract without the
receive/fallback
methods instead of an EOA to lock his/her native tokens or decides to lock on behalf of a smart contract address, instead of an EOA, he/she will not be able to unlock his/her funds after the unlockTime period. This means that his/her funds will be stuck indefinitely inside the LockManager contract. Furthermore, the call to theunlock
method will not revert, decreasing thequantity
amount of that player.This is not the same that was reported in automatic finding M-02 which describe that the transaction could fail due to the gas cost because the root cause is different.
Proof of Concept
Both
lock()
andlockOnBehalf
methods arepayable
and accept ethers. The quantity of native tokens locked by a player is saved in thelockedTokens
variable:After the lock period, they can call the unlock() method:
Thanks to line 416, the
lockedToken.quantity
is decreased by the number of native tokens requested in the_quantity
variable. However, the line 420 will not work in the right way attempting to transfer native tokens to anon-payable
contract.Even if this issue is due to a wrong implementation of the player locker contract, the fact that the quantity value is decreased even if the value is not transferred is unfair and creates an inconsistency between the
lockedToken.quantity
and the actual locked amount (the value returned by the getLocked() method is incorrect).Tools Used
Visual inspection
Recommended Mitigation Steps
We propose to allow player choosing where LockManager has to send unlocked funds:
Assessed type
ERC20