xanoutas/solidity-security-findings huggingface dataset:
Unauthorized Withdrawal of ETH by Admin in `_withdraw`.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L468
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L468-L475


# Vulnerability details

## Impact
An attacker with admin privileges can maliciously execute the `_withdraw` function, resulting in the unauthorized withdrawal of all the ETH in the contract. 

## Proof of Concept
[`_withdraw(NounsDAOStorageV3.StorageV3 storage ds) external onlyAdmin(ds) returns (uint256, bool)`](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L468): This admin-only function allows the admin to withdraw all the ETH in the contract.

[NounsDAOV3Admin.sol#L468-L475](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L468-L475)
```solidity
function _withdraw(NounsDAOStorageV3.StorageV3 storage ds) external onlyAdmin(ds) returns (uint256, bool) {
    uint256 amount = address(this).balance;
    (bool sent, ) = msg.sender.call{ value: amount }('');

    emit Withdraw(amount, sent);

    return (amount, sent);
}
```
In this function, the contract's balance is transferred to the `msg.sender` using the `.call` function. However, this code does not perform proper error handling. The issue lies in the fact that the contract blindly assumes that the `msg.sender.call` will always succeed and does not check the return value.

Here's an example scenario that i will demonstrates how the admin-only function `_withdraw` can be used to withdraw all the ETH in the contract:

1. Initialization:
   - The smart contract is deployed with an initial balance of 10 ETH.
   - The contract has an admin address assigned.

2. Admin Initiates Withdrawal:
   - The admin decides to withdraw all the ETH in the contract.
   - The admin calls the `_withdraw` function.
   - The function verifies that the caller is the admin (authorized to perform the withdrawal).
   - The function retrieves the current balance of the contract, which is 10 ETH.
   - The function transfers the entire balance (10 ETH) to the admin's address.
   - The function emits an event indicating the withdrawal amount and status.

3. After Withdrawal:
   - The contract balance is now 0 ETH.
   - The admin's address receives the withdrawn amount of 10 ETH.

References:

* Solidity Documentation on Access Control: https://docs.soliditylang.org/en/v0.8.11/contracts.html#access-control

## Tools Used
Manual Review

## Recommended Mitigation Steps
Should check the return value of the `.call` function and handle any possible errors. 

```solidity
function _withdraw(NounsDAOStorageV3.StorageV3 storage ds) external onlyAdmin(ds) returns (uint256, bool) {
    uin
Arbitrary Pending `_setPendingVetoer` Address Assignment.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L301-L309


# Vulnerability details

## Impact
If an attacker successfully impersonates the vetoer, they can set any address as the pending vetoer. This can compromise the integrity of the vetoer role and enable unauthorized access or control over certain functions or actions within the contract.

## Proof of Concept
`_setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds, address newPendingVetoer) public`: This function sets a new pending vetoer. It requires that the caller is the current vetoer. Attackers may attempt to impersonate the vetoer to set an arbitrary pending vetoer address.

In the function [`_setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds, address newPendingVetoer) public`](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L301-L309). Here is the code: 

```solidity
function _setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds, address newPendingVetoer) public {
    if (msg.sender != ds.vetoer) {
        revert VetoerOnly();
    }

    emit NewPendingVetoer(ds.pendingVetoer, newPendingVetoer);

    ds.pendingVetoer = newPendingVetoer;
}
```

The `msg.sender` is checked against `ds.vetoer` to ensure that only the current vetoer can call this function. However, the issue is that there is no further verification or authentication performed on the `newPendingVetoer` address. This means that an attacker can impersonate the current vetoer and set an arbitrary address as the pending vetoer.


## Tools Used
Manual Review.

## Recommended Mitigation Steps
Add a check to ensure that `newPendingVetoer` is not the zero address (`address(0)`) before setting it as the pending vetoer.

   ```solidity
   function _setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds, address newPendingVetoer) public {
       if (msg.sender != ds.vetoer) {
           revert VetoerOnly();
       }
+      require(newPendingVetoer != address(0), 'NounsDAO::_setPendingVetoer: new vetoer cannot be the zero address');

       emit NewPendingVetoer(ds.pendingVetoer, newPendingVetoer);

       ds.pendingVetoer = newPendingVetoer;
   }
   ```


## Assessed type

Access Control
Potential Exploitation of `_acceptAdmin` Function.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L276-L295
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L278-L281


# Vulnerability details

## Impact
If this vulnerability is exploited, an attacker can wrongfully gain access to the admin role. This can lead to unauthorized control over the contract, allowing the attacker to manipulate critical functionalities and potentially cause financial losses or disrupt the intended operation of the contract.

`_acceptAdmin` function is defined as: [NounsDAOV3Admin.sol#276-L295](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L276-L295)

```solidity
  function _acceptAdmin(NounsDAOStorageV3.StorageV3 storage ds) external {
    require(
        msg.sender == ds.pendingAdmin && msg.sender != address(0),
        'NounsDAO::_acceptAdmin: pending admin only'
    );

    address oldAdmin = ds.admin;
    address oldPendingAdmin = ds.pendingAdmin;

    ds.admin = ds.pendingAdmin;
    ds.pendingAdmin = address(0);

    emit NewAdmin(oldAdmin, ds.admin);
    emit NewPendingAdmin(oldPendingAdmin, address(0));
}
```
The function first checks that the caller `(msg.sender)` is equal to the pending admin `(ds.pendingAdmin)` and that the pending admin is not the zero address. If these conditions are met, the function proceeds to update the admin address and emit relevant events.

In the `_acceptAdmin` function: [NounsDAOV3Admin.sol#L278-L281](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol#L278-L281)

```solidity
require(
    msg.sender == ds.pendingAdmin && msg.sender != address(0),
    'NounsDAO::_acceptAdmin: pending admin only'
);
```

Attackers can manipulate the `msg.sender` and `ds.pendingAdmin` variables to bypass the authorization check and gain admin rights. The function requires that the caller (`msg.sender`) is the pending admin (`ds.pendingAdmin`) and that the pending admin is not the zero address. If these conditions are met, the admin role is transferred to the pending admin.

## Proof of Concept
Let's review the functions and assess their security implications. 

1. `_acceptAdmin(NounsDAOStorageV3.StorageV3 storage ds) external`:
This function accepts the transfer of admin rights. The vulnerability lies in the following check:
```solidity
require(
    msg.sender == ds.pendingAdmin && msg.sender != address(0),
    'NounsDAO::_acceptAdmin: pending admin only'
);
```
The vulnerability allows an attacker to manipulate the `msg.sender` and `ds.pendingAdmin` variables to gain unauthorized access to admin rights.

2. `_setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds,
Lack of Access Control in withdrawTokens Function Allows Unauthorized Token Withdrawal
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L147-L153


# Vulnerability details

## Impact
`withdrawTokens` function lacks proper access control, enabling an attacker to exploit it and withdraw tokens from the escrow to an arbitrary address, since no access control check ensures that only the DAO contract can invoke this function, and as a result of this, an attacker who gains control over the DAO contract can bypass the intended access restrictions and call the `withdrawTokens` function, transferring tokens to an unauthorized address.

## Proof of Concept
This function `withdrawTokens` allows the DAO contract to withdraw tokens from the escrow. However, there is no access control check in this function, which means any address can call this function and withdraw tokens.

[NounsDAOForkEscrow.sol#L147-L151](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L147-L153)
```solidity
function withdrawTokens(uint256[] calldata tokenIds, address to) external onlyDAO {
    for (uint256 i = 0; i < tokenIds.length; i++) {
        if (currentOwnerOf(tokenIds[i]) != dao) revert NotOwner();

        nounsToken.transferFrom(address(this), to, tokenIds[i]);
    }
}
```

Explanation:
In the `withdrawTokens` function, the loop iterates over an array of token IDs and transfers each token to the specified `to` address. However, the only check performed is `currentOwnerOf(tokenIds[i]) != dao`, which only ensures that the DAO is the current owner of the token. It does not verify if the caller is authorized to perform the withdrawal.

Exploitability:
Since there is no access control in the `withdrawTokens` function, any address can call this function and transfer tokens from the escrow to an unauthorized address. This could lead to tokens being withdrawn by an attacker who gains control over the DAO contract.

To exploit this vulnerability, an attacker would call the `withdrawTokens` function with an array of token IDs and specify their own address as the `to` parameter. 
```solidity
// Attacker contract
pragma solidity ^0.8.19;

interface NounsDAOForkEscrow {
    function withdrawTokens(uint256[] calldata tokenIds, address to) external;
}

contract Exploit {
    address public dao;  // Address of the DAO contract
    NounsDAOForkEscrow public escrow;

    constructor(address daoAddress, address escrowAddress) {
        dao = daoAddress;
        escrow = NounsDAOForkEscrow(escrowAddress);
    }

    function exploitWithdraw() external {
        uint256[] memory tokenIds = new uint256[](1);
        tokenIds[0] = 123;  // Specify the token ID to withdraw

        address attackerAddress = msg.sender;
        escrow.withdrawTokens(tokenIds, attackerAddress);
    }
}
```
The `exploitWithdraw` function calls the `withdrawTokens`
Escrow Premature Closure and Fork ID Manipulation in `closeEscrow` function.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133-L139
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L136
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133-L139


# Vulnerability details

## Impact
[`closeEscrow Function`](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133-L139):
Attack Surface: This function allows the DAO contract to close the escrow and increment the fork ID.

Attack Vectors:
Unauthorized Access: If an attacker gains control over the DAO contract, they can call this function and close the escrow prematurely or manipulate the fork ID.

[NounsDAOForkEscrow.sol#L136](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L136)
```solidity
closedForkId = forkId;
```
The `forkId` variable is incremented without any checks or validations. This means that the `forkId` value is incremented every time the `closeEscrow()` function is called, regardless of any conditions or constraints.

This can lead to issues if an attacker gains control over the DAO contract and calls the `closeEscrow()` function multiple times in quick succession. They can manipulate the `forkId` value and close the escrow prematurely, causing unintended behavior or disrupting the normal functioning of the contract.

## Proof of Concept
In the `closeEscrow()`, the function is intended to be called only by the DAO contract, but there are no access control checks to ensure this. As a result, if an attacker gains control over the DAO contract, they can call this function and manipulate the escrow closure and fork ID.
[NounsDAOForkEscrow.sol#L133-L139](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133-L139)
```solidity
function closeEscrow() external onlyDAO returns (uint32 closedForkId) {
    numTokensInEscrow = 0;

    closedForkId = forkId;

    forkId++;
}
```
The `closeEscrow()` function is declared as `external onlyDAO returns (uint32 closedForkId)`. This means that the function can only be called externally, and only by a contract that has the `onlyDAO` modifier. The `onlyDAO` modifier is a type of access control check that ensures that the function can only be called by the DAO contract.

However, the `closeEscrow()` function does not have any other access control checks. This means that if an attacker gains control over the DAO contract, they can call the `closeE
Unauthorized Access to Tokens in returnTokensToOwner function.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L120
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125


# Vulnerability details

## Impact
[`returnTokensToOwner Function`](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125):

Attack Surface: This function allows the DAO contract to unescrow tokens and return them to the original owner.
The `returnTokensToOwner` function allows the DAO contract to `unescrow` tokens by transferring them from the `NounsDAOForkEscrow` contract back to the original owner. However, the function lacks proper access control, allowing any caller to execute the function successfully.

Attack Vectors:
Unauthorized Access, If an attacker gains control over the DAO contract, they can call this function and claim tokens that do not belong to them.

## Proof of Concept
[NounsDAOForkEscrow.sol#L120](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L120)

```solidity
            nounsToken.transferFrom(address(this), owner, tokenIds[i]);
```
The issue occurs when the `returnTokensToOwner` function is called by the DAO contract to unescrow tokens and return them to the original owner. In this line of code, the tokens are transferred from the escrow contract `(address(this))` to the `owner` address without performing proper authorization checks. If an attacker gains control over the DAO contract, they can call this function and claim tokens that do not belong to them, potentially bypassing ownership checks.

[NounsDAOForkEscrow.sol#L116-L125](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125)
```solidity
function returnTokensToOwner(address owner, uint256[] calldata tokenIds) external onlyDAO {
    for (uint256 i = 0; i < tokenIds.length; i++) {
        if (currentOwnerOf(tokenIds[i]) != owner) revert NotOwner();

        nounsToken.transferFrom(address(this), owner, tokenIds[i]);
        escrowedTokensByForkId[forkId][tokenIds[i]] = address(0);
    }

    numTokensInEscrow -= tokenIds.length;
}
```


References:

* OpenZeppelin Contracts: https://github.com/OpenZeppelin/openzeppelin-contracts

## Tools Used
Manual review

## Recommended Mitigation Steps
Add an additional check to ensure that the caller of the function is the legitimate owner of the tokens be
Reentrancy `onERC721Received` and `returnTokensToOwner` function.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L94-L108
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L120
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L94-L108
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L94-L108


# Vulnerability details

## Impact
An attacker could transfer tokens from one address to another, without the permission of the token owner when exploited the reentrant call issue to manipulate the state variables of the NounsDAOForkEscrow contract, If the attacker can call the `onERC721Received` function with the attacker's contract address as the operator parameter.

## Proof of Concept
```solidity
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes memory
    ) public override returns (bytes4) {
        if (msg.sender != address(nounsToken)) revert OnlyNounsToken();
        if (operator != dao) revert OnlyDAO();


        escrowedTokensByForkId[forkId][tokenId] = from;


        numTokensInEscrow++;


        return IERC721Receiver.onERC721Received.selector;
    }
```
[onERC721Received Function:](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L94-L108)
Attack Surface: This function is called when the contract receives an ERC721 token. It is exposed as an external function and can be called by anyone, an attacker can repeatedly call `onERC721Received` function before it completes its execution. This can lead to manipulation of the contract's state, an attacker can potentially drain tokens from the escrow, disrupt the escrow process, or manipulate the contract's state in unintended ways.

Is susceptible to a reentrancy attack: [NounsDAOForkEscrow.sol#L120](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L120)
```solidity
nounsToken.transferFrom(address(this), owner, tokenIds[i]);
```
In the `returnTokensToOwner` function, If a malicious contract were to call the [`returnTokensToOwner`](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L116-L125) function and implement a reentrancy att
Exploitable Signature Forgery in castVoteBySig function.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Votes.sol#L156-L172


# Vulnerability details

## Impact
`castVoteBySig` function allows users to cast a vote for a proposal using an EIP-712 signature. An attacker could try to forge or manipulate signatures to cast unauthorized votes on behalf of other users.
```solidity
require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');
```
This is vulnerable to a signature forgery attack. It checks if the `signatory` address recovered from the signature is not equal to the null address `(address(0))`. If the condition is not met, it reverts the transaction with an error message, but the check for the null address is not sufficient to ensure the validity of the signature, because of that, an attacker can create a valid signature with a non-null address that is not authorized to cast a vote and bypass this check.

## Proof of Concept
[NounsDAOV3Votes.sol#L156-L172](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Votes.sol#L156-L172)
```solidity
    function castVoteBySig(
        NounsDAOStorageV3.StorageV3 storage ds,
        uint256 proposalId,
        uint8 support,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), block.chainid, address(this))
        );
        bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
        bytes32 digest = keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');
        emit VoteCast(signatory, proposalId, support, castVoteInternal(ds, signatory, proposalId, support), '');
    }
```
The vulnerability arises from the `ecrecover` function used to recover the signer's address from the provided signature, this can be exploited by an attacker who can forge or manipulate signatures to cast unauthorized votes on behalf of other users. The vulnerability exists because the `ecrecover` function does not verify the validity of the signature, allowing an attacker to provide a crafted signature that would result in an arbitrary signer address.

## Scenario
Let's consider a scenario where an attacker exploits the vulnerability in the `castVoteBySig` function to cast unauthorized votes on behalf of other users.
In the vulnerable contract, the `castVoteBySig` function allows users to cast a vote for a proposal using an EIP-712 signature. Here's the function:

```solidity
function castVoteBySig(
    NounsDAOStorageV3.StorageV3 storage ds,
    uint256 proposalId,
    uint8 support,
    uint8 v,
    bytes32 r,
    bytes32 s
) external {
    bytes32 do