Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 32/168
Findings: 5
Award: $616.10
🌟 Selected for report: 0
🚀 Solo Findings: 0
Specifically, ERC721Votes
enables users to delegate their voting power to others. Note that when delegating the voting power, the actual token balance will not change.
On the other hand, when transferring tokens, the voting power transfers accordingly: https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L268.
Considering the following situation.
5
tokens, so current the voting power of wallet A is 5
.0
but B is 5
. Note that currently, the balance of wallet A
remains 5
1
token to wallet B, where _moveDelegateVotes
happens again.
unchecked
statement (https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L201) enables an integer overflowtype(uint256).max
Following the code in https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Votes.sol, remove the unchecked
statement to prevent such underflow.
#0 - GalloDaSballo
2022-09-20T21:05:09Z
🌟 Selected for report: berndartmueller
Also found by: 0x52, 0xSky, Chom, PwnPatrol, bin2chen, cccz, davidbrai, elprofesor, izhuer, m9800, rvierdiiev
Specifically, to support a proposal, the voting power is counted at the time of the proposal creation time.
However, there are multiple services that support NFT flashloan, e.g., NFTuloan (https://www.nftuloan.com/).
Since the voting power is counted as the proposal creation time, a malicious proposer can first borrow a large amount of NFTs and then create the proposal, and repay the flashloan at the end of this transaction. As such, his voting power will be as large as possible.
Note that the malicious proposer still needs to hold a few token to make the proposal alive.
The attack is also enabled by another ERC721Vote
bugs (multiple voting power at the same timestamp, which I will make a separate report).
If the founder does not notice such a malicious proposal, the malicious proposal can get processed.
The bug can also be degraded as medium, since there is a time lock for a process getting effective.
Check the weight at a slightly different time compared with the proposal creation time (against flashloan)
#0 - Chomtana
2022-09-19T07:55:33Z
Dup #340
#1 - GalloDaSballo
2022-09-20T21:43:47Z
🌟 Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
61.0218 USDC - $61.02
function _addFounders(IManager.FounderParams[] calldata _founders) internal { // Cache the number of founders uint256 numFounders = _founders.length; // Used to store the total percent ownership among the founders uint256 totalOwnership; unchecked { // For each founder: for (uint256 i; i < numFounders; ++i) { // Cache the percent ownership uint256 founderPct = _founders[i].ownershipPct; // Continue if no ownership is specified if (founderPct == 0) continue; // Update the total ownership and ensure it's valid if ((totalOwnership += uint8(founderPct)) > 100) revert INVALID_FOUNDER_OWNERSHIP(); // Compute the founder's id uint256 founderId = settings.numFounders++; // Get the pointer to store the founder Founder storage newFounder = founder[founderId]; // Store the founder's vesting details newFounder.wallet = _founders[i].wallet; newFounder.vestExpiry = uint32(_founders[i].vestExpiry); newFounder.ownershipPct = uint8(founderPct); // Compute the vesting schedule uint256 schedule = 100 / founderPct; // Used to store the base token id the founder will recieve uint256 baseTokenId; // For each token to vest: for (uint256 j; j < founderPct; ++j) { // Get the available token id baseTokenId = _getNextTokenId(baseTokenId); // Store the founder as the recipient tokenRecipient[baseTokenId] = newFounder; emit MintScheduled(baseTokenId, founderId, newFounder); // Update the base token id (baseTokenId += schedule) % 100; } } // Store the founders' details settings.totalOwnership = uint8(totalOwnership); settings.numFounders = uint8(numFounders); }
When a malicious founder supplies a founderPct
bigger than 2^8, he can bypass the check against if ((totalOwnership += uint8(founderPct)) > 100) revert INVALID_FOUNDER_OWNERSHIP();
This statement (baseTokenId += schedule) % 100;
seems not helpful.
tokenAttributes[i + 1] = uint16(seed % numItems);
When numProperties
is larger than 16
, any token mint will fail.
It is suggested to use OpenZeppelin ECDSA library.
#0 - GalloDaSballo
2022-09-28T23:01:59Z
L
#1 - GalloDaSballo
2022-09-28T23:09:18Z
Dup of #303
After further thinking, because the warden didn't capture the impact of the bug, am leaving as Low.
Dup of #523
L