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: 12/168
Findings: 7
Award: $2,759.00
π Selected for report: 0
π Solo Findings: 0
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721Votes.sol#L216
Attacker could get type(uint192).max
of voting weight
So he can create a proposal()
to withdraw an amount from the Treasury.sol
and he can pass the proposalThreshold
with no need to anyone
1- Attacker buy one NFT
and transfer it to addr_1
2- addr_1
will delegate()
to addr_2
3- now Attacker will buy another NFT
and transfer it to addr_1
. So he has two 2
NFT
s on the same address addr_1
4- this time addr_1
will delegate()
to addr_3
In this step the addr_2
has type(uint192).max
of voting weight
. How?
4.1- delegate()
==>_delegate()
==> _moveDelegateVotes()
4.2- _moveDelegateVotes()
it takes 3 param
4.2.1- _from
is addr_2
. As we know his voting weight is only one 1
.
4.2.2- _to
is addr_3
4.2.3- _amount
is the amount of addr_1
which is two 2
4.3- on _moveDelegateVotes()
in the first block if (_from != address(0))
on the last line we have this calculation prevTotalVotes - _amount
as we know _amount == 2
and prevTotalVotes == 1
. So 1-2
is 2**256 - 1
because we have an unchecked
block on this function
5- on _writeCheckpoint()
the _newTotalVotes
will be only type(uint192).max
Remove the unchecked
block from _moveDelegateVotes()
#0 - GalloDaSballo
2022-09-25T21:18:48Z
Dup of #469
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721Votes.sol#L133-L135 https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721.sol#L125-L151
With only one NFT
the user can keep increasing the voting weight
on different addresses
1- letβs say Alice has one NFT
2- he delegate()
to Bob
3- Alice invoke transferFrom()
to Richard
4- Richard invoke delegate()
to Bob
So now Bob has two voting weight
We can keep doing this process until we achieve the voting weight
that we need it
When someone invokes transferFrom()
you need to undelegated his voting weight
#0 - GalloDaSballo
2022-09-26T22:51:38Z
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L128 https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L363
The proposer's voting weight
still has not dropped below the proposal threshold
but anyone can invoke cancel()
successfully
On the propose()
it just check if (getVotes(msg.sender, block.timestamp - 1) < proposalThreshold())
so you can creat a propose
in two cases =
or >
.
1- Let's say Alice creates a proposal
and their getVotes(msg.sender, block.timestamp - 1) == proposalThreshold()
2- Bob will invoke cancel()
in this check
if (msg.sender != proposal.proposer && getVotes(proposal.proposer, block.timestamp - 1) > proposal.proposalThreshold) revert INVALID_CANCEL();
3- the first part msg.sender != proposal.proposer
will be true
and the cecond part getVotes(proposal.proposer, block.timestamp - 1) > proposal.proposalThreshold
is false
. Because when Alice creates their proposal
he was have getVotes(msg.sender, block.timestamp - 1) == proposalThreshold()
4- Bob will cancel Alice even heβs voting weight still has not dropped below the proposal threshold
Even when Alice decides to invoke propose()
he has getVotes(msg.sender, block.timestamp - 1) > proposalThreshold()
but after that he can sell or transfer some tokens. He can be in this situation getVotes(msg.sender, block.timestamp - 1) == proposalThreshold()
if (msg.sender != proposal.proposer && getVotes(proposal.proposer, block.timestamp - 1) >= proposal.proposalThreshold) revert INVALID_CANCEL();
#0 - GalloDaSballo
2022-09-20T21:01:32Z
In case you want to undelegated your voting weight
you need to reset the delegation[your_Addr ] == address(0)
. But unfortunately this not working you will lose your voting weight
1- Alice delegated their voting weight
to Bob
2- after that he decides to undelegated them to use all their own voting weight
3- Alice invoke delegate(address(0))
4- on _moveDelegateVotes()
_to
param is address(0)
so it canβt pass the check if (_to != address(0))
Add more checks on _delegate()
so if _to == address(0)
you need to set it to be _to == _from
#0 - GalloDaSballo
2022-09-25T21:06:05Z
Dup of #203
π Selected for report: azephiar
Also found by: 0x52, 0xSmartContract, Ch_301, Tointer, __141345__, bin2chen, indijanc
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/token/Token.sol#L154 https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/token/Token.sol#L207-L213
It effects on the number of votes required to be in favor of a proposal in order to reach a quorum
We can see on Governor.sol
function quorum() public view returns (uint256) { unchecked { return (settings.token.totalSupply() * settings.quorumThresholdBps) / 10_000; } }
So its effects directly the proposal
On Token.sol
Every mint()
the totalSupply
is incrented by one
do { // Get the next token to mint tokenId = settings.totalSupply++; // Lookup whether the token is for a founder, and mint accordingly if so } while (_isForFounder(tokenId));
But the burn()
has no decrement ont
settings.totalSupply- -
#0 - GalloDaSballo
2022-09-16T23:23:15Z
Dup of #405
π 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
60.7743 USDC - $60.77
if (nCheckpoints != 0)
is always trueFile: /lib/token/ERC721Votes.sol if (nCheckpoints != 0) prevTotalVotes = checkpoints[_from][nCheckpoints - 1].votes; if (nCheckpoints != 0) prevTotalVotes = checkpoints[_to][nCheckpoints - 1].votes;
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721Votes.sol#L213 https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/lib/token/ERC721Votes.sol#L228
address(0)
File: /lib/token/ERC721Votes.sol function _delegate(address _from, address _to) internal { address prevDelegate = delegation[_from]; delegation[_from] = _to;
#0 - GalloDaSballo
2022-09-26T21:10:19Z
Address(0) L
Other is invalid <img width="576" alt="Screenshot 2022-09-26 at 23 10 10" src="https://user-images.githubusercontent.com/13383782/192381133-5cf5467d-12e5-4a8a-abf9-6b1588b84f70.png"> 0 ++ is 0 and then it becomes 1