xanoutas/solidity-security-findings huggingface dataset:
Potential Denial-of-Service Attack on `AccountingManager` Contract through Blacklisted Addresses
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L396-L446


# Vulnerability details

## Impact

The withdrawal process for the vault involves two key steps: first, a Keeper calculates the amount to be withdrawn, and then executes the withdrawal. However, this implementation is vulnerable to exploitation by malicious actors.

Some tokens utilized by the vault have the ability to blacklist certain addresses, prohibiting them from interacting with the token. If a user attempts to withdraw assets using a blacklisted address, the executeWithdraw function within the vault will revert due to the token's blacklist restrictions.

This vulnerability allows a malicious attacker to purposefully trigger the withdrawal process using a blacklisted address, causing the executeWithdraw function to revert. As a result, legitimate users would be unable to successfully withdraw any assets from the vault, effectively denying them access to their funds.

## Proof of Concept

The vault withdrawal process involves two key steps: first, a Keeper calculates the withdrawal amount for the user, and then the `executeWithdraw` function is called to execute the withdrawal.

Within the `executeWithdraw` function, the `AccountingManager` contract is responsible for sending the `baseToken` to the receiver address, starting from L428. However, this implementation is vulnerable to exploitation if the receiver address is blacklisted by the owner of the base token.

If the receiver address is blacklisted, the token transfer operation within the `executeWithdraw` function will revert, causing the entire withdrawal transaction to fail. This vulnerability can be exploited by a malicious actor to disrupt the withdrawal functionality of the vault, effectively denying legitimate users access to their assets.

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L428

```solidity
AccountingManager.sol
396:     function executeWithdraw(uint256 maxIterations) public onlyManager nonReentrant whenNotPaused {
             [...]
406:         while (
407:             currentWithdrawGroup.lastId > firstTemp
408:                 && withdrawQueue.queue[firstTemp].calculationTime + withdrawWaitingTime <= block.timestamp
409:                 && i < maxIterations
410:         ) {
                 [...]
428:             baseToken.safeTransfer(data.receiver, baseTokenAmount); //@audit
429:             emit ExecuteWithdraw(
430:                 firstTemp, data.owner, data.receiver, shares, data.amount, baseTokenAmount, block.timestamp
431:             );
                 [...]
435:         }
             [...]
446:     }
```

### Example Scenario

The vulnerability can be demonstrated through the following exploit scenario:

- A malicious attacker, let's call her Alice, owns an address `A` that is blacklisted by the
Proposal can be executed unlimited number of times
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L114-L130


# Vulnerability details

There is no executed flag for the proposals, so one can be executed an arbitrary number of times.

This will have critical impact, for example a proposal to send out `10 ETH` can be run `10` times, transferring `100 ETH`. Apart from the malicious Owner case, this also can be a result of an operational mistake, for example repeatedly executing the same proposal over some time if off-chain records that it was already executed were omitted.

As the functions in questions are permissioned, setting the severity to be medium.

## Proof of Concept

On queue() the proposal is marked for execution by setting the `timestamps` value for it:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L114-L130

```solidity
    /// @notice Schedules a proposal for execution
    /// @param _proposalId The proposal id
    function queue(bytes32 _proposalId) external onlyOwner returns (uint256 eta) {
        // Ensure the proposal was not already queued
        if (isQueued(_proposalId)) revert PROPOSAL_ALREADY_QUEUED();

        // Cannot realistically overflow
        unchecked {
            // Compute the timestamp that the proposal will be valid to execute
            eta = block.timestamp + settings.delay;
        }

        // Store the timestamp
        timestamps[_proposalId] = eta;

        emit TransactionScheduled(_proposalId, eta);
    }
```

There is no marking for the executed proposal, the `timestamps[proposalId]` is just deleted, so queue() can be run again right after execute():

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L141-L172

```solidity
    function execute(
        address[] calldata _targets,
        uint256[] calldata _values,
        bytes[] calldata _calldatas,
        bytes32 _descriptionHash
    ) external payable onlyOwner {
        // Get the proposal id
        bytes32 proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash);

        // Ensure the proposal is ready to execute
        if (!isReady(proposalId)) revert EXECUTION_NOT_READY(proposalId);

        // Remove the proposal from the queue
        delete timestamps[proposalId];

        // Cache the number of targets
        uint256 numTargets = _targets.length;

        // Cannot realistically overflow
        unchecked {
            // For each target:
            for (uint256 i = 0; i < numTargets; ++i) {
                // Execute the transaction
                (bool success, ) = _targets[i].call{ value: _values[i] }(_calldatas[i]);

                // Ensure the transaction succeeded
                if (!success) revert EXECUTION_FAILED(i);
            }
        }

        emit TransactionExecuted(
createBid call front-running
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#L161
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L90
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L204


# Vulnerability details

## Description

There is `createBid` function in the `Auction` contract. The function accept the `_tokenId`, which does not contain any information about the token itself. As a result, transactions of users can be front-runned to enforce user make a bid for the token with the same `_tokenId` but for which he do not want to pay.

## Proof of Concept

Someone can send two transactions to the mempool: `settleCurrentAndCreateNewAuction` and `createBid` with expected by them `_tokenId` (see note section below). Then the malicious user can send a transaction with higher fee that creates an auction for a token which have the same `_tokenId` but different content. This transaction most likely will be included in the block before the transaction from a non-malicious user. 

So, it is possible that the order of transactions will be the following:
- malicious user transaction
- non-malicious `settleCurrentAndCreateNewAuction`
- non-malicious `createBid`

Note, that it is not necessarily for the user to send two transactions to mempool simultaneously to get under attack. That also can happen while chain reorg. 

## Recommended Mitigation Steps

It is better to use a hash of content as a unique identifier. Then it would be impossible to manipulate or front-run transactions to the auction.
Attacker can gain more voting power
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L275


# Vulnerability details

## Impact
An attacker can gain more voting power at a low cost

## Proof of Concept
When voting, voting power is taken from the snapshot at the same block as the proposal was created.

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L275

The attacker can monitor mempool and borrow NFTs just in time when proposal is made. It is a low cost attack because attacker can repay the loan in the very next block, rendering the fee cost very low.



## Tools Used
Manuel review

## Recommended Mitigation Steps

 Take voting power from the prior block:

getVotes(_voter, proposal.timeCreated -1 )
The voting power cannot be returned deterministically, if there are multiple checkpoints sharing the same timestamp 
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#L207
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L222


# Vulnerability details

## Impact

Specifically, when writing a checkpoint, the code does not check whether the latest checkpoint is also in the current block.

Consider a user makes multiple times of token transfer, the `ERC721Votes` contract will record multiple checkpoints with the same timestamp.

As a result, when binary searching the checkpoint with a corresponding timestamp, the returned value is not guaranteed to be constant (which indeed depends on how many checkpoints the account has).

It will likely break any downstream applications.


## Recommended Mitigation Steps

Like https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Votes.sol, when updating checkpoints, check whether the latest one is in the same block.
NFT flashloan against governance voting
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L275


# Vulnerability details

## Impact
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. 

## Recommended Mitigation Steps

Check the weight at a slightly different time compared with the proposal creation time (against flashloan)
Potential of token lost permanently when highest bid is made by a Contract
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

When auction is happening, there is no checks on who can participate to `createBid`. In order to work well,  contract need to confirm that the Bidder is capable of receiving ERC721 or else their token may be permanently lost. 

After a bidder decided to be the highest bidder, the NFT token is transferred. But if the highest bidder is a contract which doesn't implement `onERC721Received` then, the token would be lost forever.

## Recommended Mitigation Steps

There are two possible ways to mitigate this issue:
1. Limit the Bidder only for EOA, or non contract address. (check if the msg.sender is a contract or not by using assembly extcodesize)
```
function createBid(uint256 _tokenId) external payable nonReentrant {
  ...
  if(isContract(msg.sender)) revert ONLY_EOA_BIDDER();
  ...
}

function isContract(address _addr) private returns (bool isContract){
  uint32 size;
  assembly {
    size := extcodesize(_addr)
  }
  return (size > 0);
}
```

2. When transfering the token, use safeTransferFrom compared to transferFrom to make sure if the reciver is a contract, it implements onERC721Received
The result of getPastVotes can be different time after time
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#L203-L233


# Vulnerability details

## Impact
In `_moveDelegateVotes` of `ERC721Votes.sol`, we write checkpoint every time, so there might be several checkpoints in the same timestamp. 
In that case, when we get past votes in `getPastVotes`, we use binary search by timestamp. But there might be several checkpoints with same timestamp. So the result of `getPastVotes` can vary time after time.

## Recommended Mitigation Steps
Write only one checkpoint at one timestamp.
Adversary can gain Infinite voting power due to integer overflow
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#L213


# Vulnerability details

## Impact
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.

+ Alice holds two wallet addresses, A and B
+ Wallet A gets `5` tokens, so current the voting power of wallet A is `5`.
+ Wallet A delegates all its voting power to B, so current the voting power of A is `0` but B is `5`. Note that currently, the balance of wallet `A` remains `5`
+ Wallet A transfers `1` token to wallet B, where `_moveDelegateVotes ` happens again.
     - Note that it will decrease wallet A's voting power by 1, although it is already 0.
     - The `unchecked` statement (https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L201) enables an integer overflow
     - __As a result, wallet A's voting power becomes `type(uint256).max`__


## Recommended Mitigation Steps

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.
Calling Governor.execute function without sending any ETH can withdraw general ETH funds from the Treasury contract
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L324-L345
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/treasury/Treasury.sol#L141-L172
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/treasury/Treasury.sol#L268-L269


# Vulnerability details

## Impact
When calling the following `Governor.execute` function, the `Treasury.execute` function is executed. Because `Governor.execute` is `payable`, ETH can be sent to the `Treasury` contract.

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L324-L345
```solidity
    function execute(
        address[] calldata _targets,
        uint256[] calldata _values,
        bytes[] calldata _calldatas,
        bytes32 _descriptionHash
    ) external payable returns (bytes32) {
        // Get the proposal id
        bytes32 proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash);

        // Ensure the proposal is queued
        if (state(proposalId) != ProposalState.Queued) revert PROPOSAL_NOT_QUEUED(proposalId);

        // Mark the proposal as executed
        proposals[proposalId].executed = true;

        // Execute the proposal
        settings.treasury.execute{ value: msg.value }(_targets, _values, _calldatas, _descriptionHash);

        emit ProposalExecuted(proposalId);

        return proposalId;
    }
```

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/treasury/Treasury.sol#L141-L172
```solidity
    function execute(
        address[] calldata _targets,
        uint256[] calldata _values,
        bytes[] calldata _calldatas,
        bytes32 _descriptionHash
    ) external payable onlyOwner {
        // Get the proposal id
        bytes32 proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash);

        // Ensure the proposal is ready to execute
        if (!isReady(proposalId)) revert EXECUTION_NOT_READY(proposalId);

        // Remove the proposal from the queue
        delete timestamps[proposalId];

        // Cache the number of targets
        uint256 numTargets = _targets.length;

        // Cannot realistically overflow
        unchecked {
            // For each target:
            for (uint256 i = 0; i < numTargets; ++i) {
                // Execute the transaction
                (bool success, ) = _targets[i].call{ value: _values[i] }(_calldatas[i]);

                // Ensure the transaction succeeded
                if (!success) revert EXECUTION_FAILED(i);
            }
        }

        emit TransactionExecuted(proposalId, _targets, _values, _calldatas);
    }
```

Calling `Governor.execute` is not the only way for sending ETH to the `Treasury` contract. The `Treasury` contract also accepts ETH through the following `receive` function. For a queued proposal that requires transferring values of ETH to the target addresses, the proposer can execute
Users could keep increasing the `voting weight` just by one `NFT`
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
With only one `NFT` the user can keep increasing the `voting weight` on different addresses

## Proof of Concept
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

## Recommended Mitigation Steps
When someone invokes `transferFrom()` you need to undelegated his `voting weight`
The users could duplicate their `voting weight`
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Any user who has an `NFT` could duplicate  their `voting weight` at any time 
## Proof of Concept
1- Alice has one `NFT`
2- He invokes `delegate()` and passes `_to` to his address  
3- on `_delegate()` both of ` _from == _to` are Alice addresses
4- on `_moveDelegateVotes()` `_from` is the `address(0)` , `_to` is Alice address and `_amount == 1` 
5- the first block on  `_moveDelegateVotes()` will be not execute this check `(_from != address(0))`
6- the second block will increase Alice  `voting weight` by one

## Recommended Mitigation Steps
Delegate to the same address on the `mint()`
Potential of underflow on `_moveDelegateVotes()`
Audit: 2022-09-nouns-builder-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
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

## Proof of Concept
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` 

## Recommended Mitigation Steps
Remove the `unchecked` block from `_moveDelegateVotes()`
Its possible to underflow votes using  `delegate` and `delegateBySig` on `ERC721Votes.sol`
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#L201-L216


# Vulnerability details

## Impact
Is possible to generate an underflow on [ERC721Votes.sol#L216](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L216) mainly because its wrapped in an unchecked bracked.

`prevTotalVotes` could be lower than `_amount` so this will generate and underflow;
`_writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount);`



## Proof of Concept
If user A delegates votes to B [`delegate(B)`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/lib/token/ERC721Votes.sol#L133) then if `nCheckpoints` is 0 it will underflow, and also if `prevTotalVotes` is lower than `_amount` it will underflow.

## Tools Used
Manual revision

## Recommended Mitigation Steps
First of all i think its a good practice to document use of `unchecked`. Concretely describe why it is safe to not perform arithmetic checks on each code block. Preferably for each operation.
In this particular case you could add a require to check if the operation is safe `require(prevTotalVotes >= _amount, "unsafe math");`;

```solidity
    function _moveDelegateVotes(
        address _from,
        address _to,
        uint256 _amount
    ) internal {
        unchecked {
            // If voting weight is being transferred:
            if (_from != _to && _amount > 0) {
                // If this isn't a token mint:
                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;

                    require(prevTotalVotes >= _amount, "unsafe math");

                    // Update their voting weight
                    _writeCheckpoint(_from, nCheckpoints, prevTotalVotes, prevTotalVotes - _amount);
                }
```