xanoutas/solidity-security-findings huggingface dataset:
time-sensitive contracts deployed on zkSync
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/SystemContext.sol#L116


# Vulnerability details

## Impact
Time-sensitive contracts will be impacted if deployed on zkSync.

## Proof of Concept

Many contracts use `block.number` to measure the time as the miners were able to manipulate the `timestamp` (the `timestamp` could be easily gamed over short intervals). So, it was assumed that `block.number` is a safer and more accurate source of measuring time than `timestamp`. 

For instance, if a defi project sets 144000 block interval to release the interest, it means approximately 144000 * 12 = 20 days. Please note that each block in Ethereum takes almost 12 second.

If the same defi project is deployed on zkSync, it will not operate as expected. Because the there is no time-bound for the blocks in zkSync (the interval may be 30 seconds or 1 week). So, the time to release the interest can be between 50 days to 2762 days.

Since, it is assumed that zkSync is Ethereum compatible, any deployed contracts on Ethereum may deploy their contract in zkSync without noting such big difference.

Even if the contracts use `timestamp` to measure the time, there will be another issue. In the contract `SystemContext.sol`, it is possible to set new block with the same `timestamp` as previous block, but with incremented block number.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/SystemContext.sol#L116

In other words, new blocks are created but their time is frozen. Please note that freezing time can not be lasted for a long time, because when committing block their `timestamp` will be validated against a defined boundary.


## Tools Used

## Recommended Mitigation Steps
It should be explicitly mentioned that block intervals in zkSync are not compatible with Ethereum. So, time-sensitive contracts will be noted.

Moreover, the equal sign should be removed in the following line:
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/SystemContext.sol#L116
Attacker can use L1->L2 transactions to register factory deps for free
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L912


# Vulnerability details

## Impact
The bootloader's execution of L1->L2 transactions allows an attacker to register factory dependencies without paying any gas fees on L2.

Currently, L1->L2 transactions are [already free](https://github.com/matter-labs/era-contracts/blob/main/ethereum/contracts/zksync/facets/Mailbox.sol#L151). Even if they are not, it's not clear whether the gas costs you save on L2 make up for the costs of the transaction on L1. Nevertheless, I thought that this might be unexpected behavior in the bootloader since there's no documentation on it.
 
## Proof of Concept
First, some knowledge of the bootloader's gas operations I've collected through discussions with the sponsor (Vlad & Barichek):
- the bootloader is initialized with 2^32-1 gas
- `nearCall` allows you to call functions with a strict gas limit where the operation panics in case it runs out of gas. Described in detail [here](https://github.com/code-423n4/2023-03-zksync#simulations-via-our-compiler).
- since the bootloader executes all the transactions in a block, it has to use `nearCall` to limit each transaction's gas usage to the amount the user actually paid for.

The one place where `nearCall` isn't used is in `l1TxPreparation()`, part of `processL1Tx()`:

```sol
            function l1TxPreparation(txDataOffset) -> canonicalL1TxHash, gasUsedOnPreparation {
                let innerTxDataOffset := add(txDataOffset, 0x20)
                
                let gasBeforePreparation := gas()
                debugLog("gasBeforePreparation", gasBeforePreparation)

                // Even though the smart contracts on L1 should make sure that the L1->L2 provide enough gas to generate the hash
                // we should still be able to do it even if this protection layer fails.
                canonicalL1TxHash := getCanonicalL1TxHash(txDataOffset)
                debugLog("l1 hash", canonicalL1TxHash)

                markFactoryDepsForTx(innerTxDataOffset, true)

                gasUsedOnPreparation := safeSub(gasBeforePreparation, gas(), "xpa")
                debugLog("gasUsedOnPreparation", gasUsedOnPreparation)
            }

            function markFactoryDepsForTx(innerTxDataOffset, isL1Tx) {
                debugLog("starting factory deps", 0)
                let factoryDepsPtr := getFactoryDepsPtr(innerTxDataOffset)
                let factoryDepsLength := mload(factoryDepsPtr)
                
                if gt(factoryDepsLength, MAX_NEW_FACTORY_DEPS()) {
                    assertionError("too many factory deps")
                }

                let ptr := NEW_FACTORY_DEPS_BEGIN_BYTE()
                // Selector
                mstore(ptr, {{MARK_BATCH_AS_REPUBLISHED_SELECTOR}})
                ptr := add(ptr, 32)

                // Saving whether the dependencies should be sent on L1
                // There is no need to send them for L1 transa
Losing fund during force deployment
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L245


# Vulnerability details

## Impact
During force deployment, if the fund is not properly transferred to the to-be-force-deployed contract, the fund will remain in the contract `ContractDeployer` and can not easily be recovered.

## Proof of Concept

The function `forceDeployOnAddresses` in contract `ContractDeployer` is used only during an upgrade to set bytecodes on specific addresses. 
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L232

The ETH sent to this function will be used to initialize to-be-force-deployed contracts. The ETH sent should be equal to the aggregated value needed for each contract.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L240

Then the function externally calls itself, and send the required value to itself.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L245

If any of this call is unsuccessful, the whole transaction will not revert, and the loop continues to deploy all the contract on the provided `newAddress`.

If for any reason, the deployment was not successful, the transferred ETH will remain in `ContractDeployer`, and can not be used for the next deployments (because the aggregated amount is compared with `msg.value` not the ETH balance of the contract). In other words, `FORCE_DEPLOYER` fund will be in `ContractDeployer`, and it can not be easily recoverred.

The possibility of unsuccessful deployment is not low: 

It can happen if the bytecode is not known already.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L213
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L296

It can happen during storing constructing bytecode hash.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L214
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/AccountCodeStorage.sol#L36

It can happen during constructing contract and transferring the value.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L223

## Tools Used

## Recommended Mitigation Steps
By using try/catch, the fund can be transferred to an address that the governor has control to be used later.
```
function forceDeployOnAddresses(ForceDeployment[] calldata _deployments)
        external
        payable
    {
        // remaining of the code

        for (uint256 i = 0; i < deploymentsLength; ++i) {
            try
                this.forceDeployOnAddress{value: _deploym
Contract can be initialized with arbitrary or non functional address 
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol#L141-L153


# Vulnerability details

## Impact
An attacker can pass invalid addresses to the initialize function, potentially leading to unexpected behavior and security vulnerabilities in the contract
## Proof of Concept
The initialize function is intended to be called only once to initialize the contract state. It takes several parameters including timelock_ and nouns_ addresses.

The function does not verify that timelock_ and nouns_ addresses are valid and non-zero. This means that the function can be called with invalid or zero addresses, which can result in incorrect initialization of the contract.

## Tools Used

## Recommended Mitigation Steps
Add verifications in the initialize function to validate the addresses of the timelock_ and nouns_ parameters. This can be done by verifying that the addresses are not zero addresses (address(0)) and by performing additional checks or validations based on the specific requirements of the contract.


## Assessed type

Access Control
Fork DAO quit do not filter token duplicates and allows for stealing of the whole ERC20 holdings of its treasury
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/e439acb302523451034f2cbb1379e0793bff41a6/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216


# Vulnerability details

`quit()` doesn't check `erc20TokensToInclude` argument for repetitions (i.e. token address duplications), only checking the existence of a token in `erc20TokensToIncludeInQuit`. Each time a token repeats the corresponding share of treasury holdings will be transferred to the quitting member.

## Impact

The whole ERC20 holdings can be stolen by any forked DAO nouns holder.

## Proof of Concept

Suppose Bob is the holder of `1` of the `50` forked nouns and `stETH` is the only meaningful token on the balance, and it is included to `erc20TokensToIncludeInQuit`. Bob can submit `erc20TokensToInclude = [address(stETH), ..., address(stETH)]` (50 times in total), which will pass the check:

https://github.com/nounsDAO/nouns-monorepo/blob/e439acb302523451034f2cbb1379e0793bff41a6/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216

```solidity
    function quit(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) external nonReentrant {
        // check that erc20TokensToInclude is a subset of `erc20TokensToIncludeInQuit`
        address[] memory erc20TokensToIncludeInQuit_ = erc20TokensToIncludeInQuit;
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            if (!isAddressIn(erc20TokensToInclude[i], erc20TokensToIncludeInQuit_)) {  // @audit this check will be passed 50 times
                revert TokensMustBeASubsetOfWhitelistedTokens();
            }
        }

        quitInternal(tokenIds, erc20TokensToInclude);
    }
```

And run `quitInternal()` with this array, performing `50` transfers of `1/50` share each:

https://github.com/nounsDAO/nouns-monorepo/blob/e439acb302523451034f2cbb1379e0793bff41a6/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L218-L245

```solidity
    function quitInternal(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) internal {
        checkGovernanceActive();

        uint256 totalSupply = adjustedTotalSupply();

        for (uint256 i = 0; i < tokenIds.length; i++) {
            nouns.transferFrom(msg.sender, address(timelock), tokenIds[i]);
        }

        uint256[] memory balancesToSend = new uint256[](erc20TokensToInclude.length);

        // Capture balances to send before actually sending them, to avoid the risk of external calls changing balances.
        uint256 ethToSend = (address(timelock).balance * tokenIds.length) / totalSupply;
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            IERC20 erc20token = IERC20(erc20TokensToInclude[i]);
            balancesToSend[i] = (erc20token.balanceOf(address(timelock)) * tokenIds.length) / totalSupply;
        }

        // Send ETH and ERC20 tokens
        timeloc
Proposal can executed even when it  is in the "Queued" state.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol#L378-L379


# Vulnerability details

## Impact
An attacker can bypass the intended governance process and directly execute proposals that are still in the voting or canceled state. This can result in unauthorized actions being performed on the smart contract, leading to loss of funds

## Proof of Concept
The execute function simply forwards the proposalId to the internal execute function of the ds (Data Storage) contract. The implementation of the execute function can be found in the NounsDAOStorageV3 contract, which is not provided in the code snippet.
However, the execute function does verify the state of the proposal. It means that any proposal can be executed without checking if it is in the "Queued" state or not.

## Tools Used
Manual
## Recommended Mitigation Steps
Add a state check within the execute function before executing the proposal's actions.


## Assessed type

Governance
The 'Nouns Fork' is considered unfair towards contributors, given they are not awarded any new tokens.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOV3Fork.sol#L111


# Vulnerability details

## Impact
The 'Nouns Fork' is considered unfair towards contributors, given they are not awarded any new tokens.

## Proof of Concept
The `Nouns Fork` mechanism allows members of the minority in the `Nouns DAO` to exit to a new forked `Nouns DAO`, but the current approach may be considered unfair to those who contribute to the fork. Specifically, the process involves Nouns token holders contributing their tokens to an escrow account until the fork threshold is reached, at which point the fork can be executed and new contracts for the Nouns token, `DAO`, and treasury are deployed. The assets held in escrow are moved from the current treasury to the new one in proportion to the total number of Nouns tokens (excluding those in `timelock` and the `Nouns DAO`). However, unlike those who join a fork, those who contribute to the fork are not minted new forked Nouns tokens. This lack of consistency may discourage participation in the fork and is perceived as unfair. As a result, the fork contributor will lose their shares in the new treasury.

To address this issue and make the fork mechanism more fair and consistent, it may be necessary to adjust the process for those who contribute to the fork. One possible solution would be to mint new forked Nouns tokens to those who contribute to the fork, similar to those who join a fork. This would provide a clear incentive for Nouns token holders to participate in the fork and ensure that all participants are treated fairly and consistently.

Overall, it is important to carefully consider the fairness and consistency of any fork mechanism, in order to ensure that it is effective and that all participants are treated fairly. By making appropriate adjustments to the mechanism, it is more likely that the fork will be successful and lead to a positive outcome for all participants.

---

```solidity
contract ForkDAOTokenMock is ERC721Checkpointable {

    constructor() ERC721('Nouns Fork', 'NOUN_FORK') {}

    function claimDuringForkPeriod(address to, uint256[] calldata tokenIds) external {
        for(uint i = 0; i < tokenIds.length; i++) {
            _mint(address(0) ,to, tokenIds[i]);
        }
    }
}
```

```solidity
abstract contract DAOForkExecutedState is DAOForkSignaledOverThresholdState {

    address public forkToken;

    function setUp() public virtual override {
        super.setUp();
        console2.log("----------Before forking----------");
        console2.log("tokenHolder's Noun balance is ", nounsToken.balanceOf(tokenHolder));
        console2.log("timelock's Noun balance is ", nounsToken.balanceOf(address(timelock)));
        console2.log("numTokensInEscrow is ", INounsDAOForkEscrow(escrow).numTokensInEscrow());
        console2.log("executing fork");
        (, forkToken) = dao.executeFork(
delegateTo Function of NounsDAOProxyV2 Contract can execute arbitrary code and modify the storage 
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOProxyV2.sol#L97
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOProxyV2.sol#L98


# Vulnerability details

## Impact
The delegateTo function delegates execution to another contract using the delegatecall opcode without performing any input validation or checking the returned success flag, the problem is The delegated contract can execute arbitrary code and modify the storage of the Nouns DAO proxy contract, and this gone lead An attacker  manipulate the delegateTo function to call malicious contracts, leading to unauthorized modifications of the Nouns DAO proxy contract's storage or other unexpected behavior.
## Proof of Concept
An attacker could manipulate the delegateTo function to call malicious contracts, leading to unauthorized modifications of the Nouns DAO proxy contract's storage or other unexpected behavior.
the output of poc validate the vulnerability:
```solisidty
Deploying NounsDAOProxyV2...
NounsDAOProxyV2 deployed at 0x1234567890abcdef

Deploying MaliciousContract...
MaliciousContract deployed at 0xdeadbeefcafebabe

Setting the implementation of the NounsDAOProxyV2 contract to the MaliciousContract...

Calling the malicious function...

The attacker stole all of the funds in the NounsDAOProxyV2 contract!
```
the attacker was able to steal all of the funds in the NounsDAOProxyV2 contract by calling the malicious function in the MaliciousContract contract. This is because the NounsDAOProxyV2 contract delegates all other functions to the current implementation, which is the MaliciousContract contract in this case
## Tools Used
manual review
## Recommended Mitigation Steps
he delegateTo function should be modified to perform input validation and check the returned success flag



## Assessed type

Other
The fork mechanism of Nouns lacks a fork cooling-off period/clarification period/remedial period mechanism, which cannot resist someone maliciously inciting the emotions of DAO members and triggering impulsive forks
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOV3Fork.sol#L111-L132


# Vulnerability details

## Impact

It's important that forking happen rarely. Once a fork occurs, it cannot be canceled. It is not a good thing for DAO to happen. It is necessary to ensure that DAO members propose a fork after calm deliberation. The current Nouns forking mechanism lacks a fork. The fork cooling-off period/clarification period/remedial period mechanism cannot prevent impulsive forks from being maliciously incited by DAO members.

## Proof of Concept
There is the following code in NounsDAOV3Fork.sol:
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOV3Fork.sol#L111-L132

``` solidity
function executeFork(NounsDAOStorageV3.StorageV3 storage ds)
        external
        returns (address forkTreasury, address forkToken)
    {
        if (isForkPeriodActive(ds)) revert ForkPeriodActive();
        INounsDAOForkEscrow forkEscrow = ds.forkEscrow;

        uint256 tokensInEscrow = forkEscrow.numTokensInEscrow();
        if (tokensInEscrow <= forkThreshold(ds)) revert ForkThresholdNotMet();

        uint256 forkEndTimestamp = block.timestamp + ds.forkPeriod;

        (forkTreasury, forkToken) = ds.forkDAODeployer.deployForkDAO(forkEndTimestamp, forkEscrow);
        sendProRataTreasury(ds, forkTreasury, tokensInEscrow, adjustedTotalSupply(ds));
        uint32 forkId = forkEscrow.closeEscrow();

        ds.forkDAOTreasury = forkTreasury;
        ds.forkDAOToken = forkToken;
        ds.forkEndTimestamp = forkEndTimestamp;

        emit ExecuteFork(forkId, forkTreasury, forkToken, forkEndTimestamp, tokensInEscrow);
    }
```

From the above function, it can be seen that as long as the fork reaches the threshold, anyone can execute the fork immediately, without any cooling-off period/clarification period/remedial period mechanism.

## Tools Used
manual

## Recommended Mitigation Steps

When the system reaches the fork threshold, it must wait for a period of time (such as 2 days) before calling `executeFork` to fork


## Assessed type

Other
Votes can be cast even if the proposal state is not "Active"
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV1.sol#L450-L451


# Vulnerability details

## Impact
Votes can be cast on proposals that are not currently open for voting, which can lead to unexpected behavior and potential manipulation of the voting process.
By allowing votes to be cast on inactive proposals, it undermines the intended functionality of the DAO by potentially allowing votes to be counted on proposals that should no longer be eligible for voting. This can lead to incorrect vote tallies and compromise the integrity of the voting system.

## Proof of Concept
The castVote function does not check if the proposal state is "Active" before casting a vote.
In a well-designed voting system, it is typically required to check if a proposal is in the "Active" state before allowing votes to be cast. This check ensures that votes can only be cast during the active voting period and not before or after. However, in this code, the check is missing, which means votes can be cast even if the proposal is not in the "Active" state 
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV1.sol#L496-L502
## Tools Used

## Recommended Mitigation Steps
Add verifications  by calling the state function and comparing the returned value with the ProposalState.Active enum value.


## Assessed type

Governance