Platform: Code4rena
Start Date: 20/09/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 198
Period: 3 days
Judge: 0xean
Total Solo HM: 2
Id: 164
League: ETH
Rank: 35/198
Findings: 2
Award: $229.38
🌟 Selected for report: 2
🚀 Solo Findings: 0
🌟 Selected for report: Czar102
Also found by: 0xDecorativePineapple, 0xNazgul, 0xSky, 0xbepresent, 0xmatt, Atarpara, Bahurum, DimitarDimitrov, Franfran, GimelSec, JGcarv, JLevick, Junnon, OptimismSec, Rolezn, Ruhum, Soosh, Tomo, Trust, __141345__, adriro, ajtra, bin2chen, cRat1st0s, cccz, cryptonue, d3e4, innertia, jag, joestakey, neumo, obront, pashov, pauliax, pcarranzav, peanuts, rajatbeladiya, rbserver, reassor, seyni, wagmi, zzykxx, zzzitron
0.7375 USDC - $0.74
The admin of the token is not constrained to minting maxSupply_
, they can mint any number of tokens.
// If we're using maxSupply, we need to make sure we respect it // mintableSupply = 0 means mint at will if(mintableSupply > 0) { require(amount <= mintableSupply, "INVALID_AMOUNT"); // We need to reduce the amount only if we're using the limit, if not just leave it be mintableSupply -= amount; }
The logic is as follows: if the amount that can be minted is zero, treat this as an infinite mint. Else require that the minted amount is not larger than mintable supply.
One can note that it is possible to mint all mintable supply. Then the mintable supply will be 0
which will be interpreted as infinity and any number of tokens will be possible to be minted.
Manual analysis
Treat 2 ** 256 - 1
as infinity instead of 0
.
#0 - 0xean
2022-09-23T23:49:27Z
The wardens logic is correct, but given that this behind an admin only flag there are some external factors that would need to come into play for this to be realized. Downgrading to M severity.
#1 - lucyoa
2022-09-27T18:11:34Z
IMHO vulnerabilities related to the protocol not working correctly or not as advertised should be considered high severity.
In this case advertised limited supply tokens do not work correctly and users are mislead. Imagine users buying tokens with "limited supply" and then info about the vulnerability becomes public leading to sell pressure and effectively loss for users.
🌟 Selected for report: Czar102
Also found by: 0xSmartContract, Lambda, Respx, csanuragjain, hansfriese, sashik_eth
228.641 USDC - $228.64
https://github.com/code-423n4/2022-09-vtvl/blob/main/contracts/VTVLVesting.sol#L394-L411
If the token is reentrant, an admin can steal all tokens locked in the VTVLVesting
contract while having active locks.
In other words, due to this exploit possibility, the contract may be insolvent with respect to active vestings. Note that revoking claim doesn't break this invariant since the vesting is closed in that case.
The reentrancy in the vested token can be used by an admin if the execution can be hijacked before the balance change occurs.
/** @notice Admin withdrawal of the unallocated tokens. @param _amountRequested - the amount that we want to withdraw */ function withdrawAdmin(uint112 _amountRequested) public onlyAdmin { // Allow the owner to withdraw any balance not currently tied up in contracts. uint256 amountRemaining = tokenAddress.balanceOf(address(this)) - numTokensReservedForVesting; require(amountRemaining >= _amountRequested, "INSUFFICIENT_BALANCE"); // Actually withdraw the tokens // Reentrancy note - this operation doesn't touch any of the internal vars, simply transfers // Also following Checks-effects-interactions pattern tokenAddress.safeTransfer(_msgSender(), _amountRequested); // Let the withdrawal known to everyone emit AdminWithdrawn(_msgSender(), _amountRequested); }
Let's consider function withdrawAdmin
. Firstly, the balance is checked and then if there is enough token surplus to withdraw, the withdrawal is allowed. The surplus is based on two values: numTokensReservedForVesting
which isn't changed by this function and the balance of the contract.
If the owner hijacks the execution before the balance change in the token transfer (which is possible in, for example, ERC777), an admin can call this function again and it will allow for an invocation of another transfer since the token balance hasn't changed yet.
For example, if there is $1m in vestings in the contract, an admin can send $100k to it in tokens and recursively invoke withdrawalAdmin
with the amount of $100k eleven times so that the whole contract balance will be drained.
Manual analysis
Add ReentrancyGuard
's nonReentrant
to the withdrawAdmin
function.
#0 - 0xean
2022-09-24T20:55:16Z
This would require a number of assumptions to be the case including a malicious admin which the sponsors called out of scope. Because it is obviously not intended functionality, I am going to leave as M pending sponsor review. I think the non-reentrant modifier is worth adding.
#1 - lawrencehui
2022-10-07T15:14:32Z
Will consider adding ReentrancyGuard as suggested.