xanoutas/solidity-security-findings huggingface dataset:
Potential Denial of Service (DoS) Vulnerability Due to Block Gas Limit Exhaustion in `_delegateMulti` Function
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol?#L85-L96


# Vulnerability details

## Overview
The function `_delegateMulti` is architected to process arrays of sources, targets, and amounts to manage delegations within a smart contract. However, the function may potentially exhibit a Denial of Service (DoS) vulnerability due to the block gas limit in the Ethereum network, particularly within the `for` loop that iterates through the provided arrays.

## Impact
If malicious actors create unusually large arrays for sources, targets, and amounts that cause the loop to iterate a high number of times, the gas required to process the function could exceed the block gas limit. This would result in a failed transaction, and if done persistently, could cause a DoS condition rendering the function unusable. This vulnerability could disrupt the normal operation of the contract and may incur financial loss to honest users due to failed transactions and wasted gas fees.

## Proof of Concept
* https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol?#L85-L96
```solidity
// The vulnerable part is the for loop that iterates based on the length of the input arrays
for (
    uint transferIndex = 0;
    transferIndex < Math.max(sourcesLength, targetsLength);
    transferIndex++
) {                    
    address source = transferIndex < sourcesLength 
        ? address(uint160(sources[transferIndex]))
        : address(0);
    address target = transferIndex < targetsLength 
        ? address(uint160(targets[transferIndex]))
        : address(0);
    uint256 amount = amounts[transferIndex];
    // ... rest of the code
}
```
In the code snippet above, the `for` loop's iteration count is dictated by the maximum length among the `sources`, `targets`, and `amounts` arrays. An attacker could provide exceptionally large arrays to maximize the iteration count, thereby increasing the gas required to a point where it exceeds the block gas limit and causes the transaction to fail.

Hardhat POC:
Add this function to the `delegatemulti.js` test:

```
  describe('DOS due to gas block limit', () => {
    it('DOS Due to gas block limit POC', async () => {
        
        const Value = "0x1000000000000000000000000";  
        const delegatorTokenAmount = await token.balanceOf(deployer);

        // give allowance to multi delegate contract
        await token.approve(multiDelegate.address, delegatorTokenAmount);

        // Create a very long list of delegates
        // The length of the arrays is increased to potentially exceed the block gas limit
        const targets = new Array(10000).fill(Value);  
        const sources = new Array(10000).fill(Value);

        // Create a long list of large values
        const amounts = new Array(10000).fill(100);

        try {
            // Try to execute the delegateMulti function with the long lists
            const tx = await multiDelegate.delegateMul
Unvalidated delegate input allows brute-force collision of proxy contract addresses.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L198-L215
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L204


# Vulnerability details

## Impact

Retrieving the proxy contract addresses relies on the delegate being a valid one approved by the contract.
Need to verify delegate is on the approved list before retrieving the address.

## Proof of Concept

**[The issue occurs in the retrieveProxyContractAddress function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L198-L215)**:

```solidity
function retrieveProxyContractAddress(
  ERC20Votes _token, 
  address _delegate
) private view returns (address) {

  // Issue is here:
  bytes32 hash = keccak256(abi.encodePacked(
    bytes1(0xff), 
    address(this), 
    uint256(0),  
    keccak256(bytecode)
  ));
  
  return address(uint160(uint256(hash)));
}
```

Specifically where the **[_delegate](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L204)** parameter is passed directly into the hash without validation.

This could allow an attacker to brute force delegate addresses to try and collide with a desired hash.

As we can see, `retrieveProxyContractAddress` function is used to retrieve the address of a proxy delegate contract in a deterministic way using create2. 

It takes a delegate address as input and uses that plus other parameters to generate a deterministic address that maps to that (token, delegate) pair.

The issue is that the delegate address is not validated before being used in the create2 hash generation. This allows an attacker to brute force delegate addresses in order to collide with a desired create2 contract address.

For example, an attacker could pass incremental values for the delegate address:

```solidity
retrieveProxyContractAddress(0x1111...) 
retrieveProxyContractAddress(0x1112...)
retrieveProxyContractAddress(0x1113...)
```

Until they collide with a desired address and retrieve that contract.

This is possible because the create2 address depends on the contract state. By iterating through delegate addresses, an attacker can iterate through create2 outputs.

The root cause is using unvalidated external input in a deterministic hash generation. 


Here is a unit test that could validate the ability to brute force delegate addresses and retrieve different proxy contract addresses:

```js
const { expect } = require("chai");

describe("retrieveProxyContractAddress", () => {

  it("Should return different addresses when brute forcing delegates", async () => {

    const delegates = [
      "0x111...",
      "0x112...", 
      "0x113..."
    ];

    for(let i = 0; i < delegates.length; i++) {
      const address = await contract.retrieveProxyContractAddress(delegates[i]);
Allowing arbitrary external input to control a deterministic lookup.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L124-L134
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L134
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L198-L201


# Vulnerability details

## Impact

Transferring between proxy delegators relies on the input delegates being valid. There should be a check that the source and target are approved delegate addresses before calling `transferBetweenDelegators`.

## Proof of Concept

The transfer logic occurs **[in the _processDelegation function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L124-L134)**:

```solidity
function _processDelegation(
  address source, 
  address target,
  uint256 amount
) internal {

  // ...

  transferBetweenDelegators(source, target, amount);

  // ...

}
```

The problem is that the source and target are not validated before being passed to `transferBetweenDelegators` on **[line 134](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L134)**.

The `transferBetweenDelegators` function is used to transfer voting power between two proxy delegate contracts. It takes a source and target address as input. 

As the issue is that these source and target addresses are not validated before being passed into the `retrieveProxyContractAddress` lookup. This lookup uses `create2` to deterministically generate an address for a proxy delegate contract given the token address and delegate address.

The problem is that by not validating the source and target, an attacker could provide any arbitrary address and retrieve any contract address they want. For example:

```solidity
transferBetweenDelegators(
  0x1234567890, // invalid address
  0x1234567890, // invalid address
  100
) 
```

This would let the attacker lookup the create2 address for any pair of addresses, even if they are not valid delegates. This is dangerous because the create2 address depends on the contract state, so the attacker could brute force delegate addresses until they find a desired address to retrieve.

The root cause is passing external input directly into a sensitive lookup without first validating it. 

Here are a few ways I could demonstrate evidence of this issue:

Code snippets:

I can point to the specific code lines that show the lack of input validation: **[function transferBetweenDelegators](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e1414
Amounts not checked for out of bounds can lead to unintended consequences
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L116


# Vulnerability details

## Impact
The issues comes from the fact that, A delegator can be passing more amounts in the array than the source array in the _delegateMulti function, which can make way for unintended consequences for the caller of the function, let me explain.

The function is pretty implemented well, it states that if they are no sources entered in the array then the caller of the function has not yet delegated their votes to a delegate yet through the proxy contract, so the caller of the function is the one to distribute hist vote tokens to all delegates, pretty solid !, but this functionality also might have some consequences also, 

## Proof of Concept
If a user has already delegated and they want to redelegate again, which means they might want to transfer from one proxy contract to another for whatever reason they might have, them entering a number of sources and also a number of targets with the amounts intended, if a user just intendeds to send from those sources alone and more amounts are entered than the source array, then we have a situation were if the running code were to get to the point of the remaining targets and amounts The user will have the pay from their own tokens to pay the remaining targets, but it could get more problematic if the targets were already a proxy that holds tokens for a delegate, then that will be spending more money than the user intended, instead of transferring the amount from their other proxy, a new set of tokens will be transferred,

Lets Take a Look at this Example

Delegator alice wants to delegate her vote tokens from other proxy contracts to target contracts, 
She enters sources 

Sources = [0, 1, 2, 3]
Targets = [0, 1, 2, 3, 4]
amounts= [100, 120, 150, 110, 450]

```solidity
 else if (transferIndex < targetsLength) {
       // Handle any remaining target amounts after the transfer process.
        createProxyDelegatorAndTransfer(target, amount);
      }
```

When she calls the multiDelegate function the first 4 operation performs as stated in function, but on the last target and amount we can see their is no sources for that, which makes the msg.sender the one liable one to delegate their own tokens to the last target, which can be a lot of tokens compared to the previous delegations

``` solidity
 function createProxyDelegatorAndTransfer(
        address target,
        uint256 amount
    ) internal {
        address proxyAddress = deployProxyDelegatorIfNeeded(target);
        token.transferFrom(msg.sender, proxyAddress, amount);
    }
```
 

## Tools Used
Manual Review

## Recommended Mitigation Steps

I cannot ask the protocol developer to check if there is a source to cover every amount that is entered in the amounts array, because that would go against the single implementation function they want to implement, that the first time a us
`DelegateMulti` lacks array length validation, potential overflow, and vulnerability.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L61


# Vulnerability details

## Impact

The `delegateMulti` function takes array inputs for sources, targets, and amounts. This could be vulnerable to an overflow if the array lengths are not properly checked. 

## Proof of Concept

**[The issue is in the delegateMulti function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L61)**:
```solidity
function delegateMulti(
  uint256[] calldata sources, 
  uint256[] calldata targets,
  uint256[] calldata amounts
) external {
  // ...
}
```

Specifically, this function takes array parameters for sources, targets, and amounts but does not check that the array lengths match before operating on them. 

The validation should occur on like:

```solidity
  require(
    sources.length == targets.length && sources.length == amounts.length, 
    "Array lengths must match"
  );

  require(sources.length > 0, "No sources provided");
```

Adding these two require statements before looping through the arrays would prevent any potential overflow or unintended behavior from array length mismatch.

The key is validating that array lengths match and are non-zero before trusting the array inputs within the loop. 

---

The potential vulnerability arises because these arrays are passed in separately, so there is no guarantee they will be the same length.

**For example, a user could pass:**

```solidity
sources = [1,2,3] 
targets = [4,5]
amounts = [10,20,30]
```

Now in the loop, it will try to do:

- Transfer 10 tokens from source 1 to target 4 
- Transfer 20 tokens from source 2 to target 5
- Transfer 30 tokens from source 3 to ??? 

Since targets array only has two elements, it leads to unintended behavior on the 3rd loop iteration. 

This could be exploited by a malicious user to:

- Overflow targets array and transfer to an unintended delegate
- Overflow amounts array and withdraw unexpected tokens
- Crash the contract by referencing invalid array indexes

The root cause is a lack of proper validation on the array lengths before operating on them. 

But it'll be fixed by adding checks like:

```solidity
require(sources.length == targets.length); 
require(sources.length == amounts.length);
require(sources.length > 0); 
```

This way it ensures the array lengths match and are non-zero before the loop. This prevents any overflow or out-of-bounds behavior when processing the arrays in parallel.

So In summary, the core issue is untrusted input validation on array lengths, which could lead to unintended logic errors or exploits during the looped delegation transfer logic.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Please see Proof of Concept.

Should validate array lengths match and are non-zero.


## Assessed type

Invalid Validation
Incorrect Proxy Contract Address Retrieval in _reimburse Function
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L144


# Vulnerability details

## Description
In the function `_reimburse` function, there is a bug that causes the incorrect proxy contract address to be retrieved. 

The bug occurs when the `source` delegate is used to retrieve the proxy contract address by calling the `retrieveProxyContractAddress` function. However, the `retrieveProxyContractAddress` function expects the `ERC20Votes` contract instance and the `delegate` address to be passed as parameters. Unfortunately, the `token` variable (which is an instance of `ERC20Votes`) is not passed as a parameter to the function. As a result, an incorrect proxy contract address will be retrieved, leading to incorrect token transfers.

## Impact
This bug can have significant consequences for the functionality of the `_reimburse` function. Since an incorrect proxy contract address is obtained, any transfers made to that address will be erroneous. This could result in the loss of tokens or incorrect distribution of funds, which may cause financial losses for users.



Proof of Concept:
To demonstrate the bug, consider the following code snippet:


     function _reimburse(address source) external {
         // Incorrect usage of retrieveProxyContractAddress without passing the 'token' parameter
         address proxyAddressFrom = retrieveProxyContractAddress(token, source);
    
         // Incorrect token transfer logic using the obtained incorrect proxy address
         ERC20Votes proxyFrom = ERC20Votes(proxyAddressFrom);
         proxyFrom.transferFrom(source, address(this), amount);
    
         // Rest of the function code...
     }


In the code, by not passing the `token` parameter to the `retrieveProxyContractAddress` function, an incorrect proxy contract address will be retrieved, leading to incorrect token transfers.

## Recommendation and Suggested Fix
To fix this bug, the following steps can be taken:

1. In the `_reimburse` function, modify the line where `retrieveProxyContract` is called. Pass the `token` variable as a parameter when retrieving the proxy contract address. Replace the following line:

      address proxyAddressFrom = retrieveProxyContractAddress(token, source);

with:
     
      address proxyAddressFrom = retrieveProxyContractAddress(source);


2. Modify the `retrieveProxyContractAddress` function to remove the `_token` parameter since the `token` variable is already accessible within the contract. Replace the following line in the contract:

     function retrieveProxyContractAddress(
         ERC20Votes _token,
         address _delegate
     ) private view returns (address) {

with:

     function retrieveProxyContractAddress(
         address _delegate
     ) private view returns (address) {


By making these changes, the bug will be fixed, and the correct proxy contract address will be used for token transfers in the `_reimburse` function. This will ensure that to
Excessive event emission risks spam, fake data, and potential overflows.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L32
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L187
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L33-L37
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L124-L137
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L32
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L187


# Vulnerability details

## Impact

There are various events emitted for delegation transfers and proxy deployments. This improves transparency, yes, but While events can improve transparency, over-emitting certain event data could potentially expose vulnerabilities.

Here are some ways malicious actors could try to exploit the delegation transfer and proxy deployment events in this contract:

- Spamming the events with dummy data to obfuscate actual delegations or make it difficult to find real events in the log.

- Emitting events that mimic real delegations to falsify voting power or balances.

- If event data lacks validation, emitting values that cause overflows or other unexpected behavior.

- Using rapid delegations/deployments to try and lag or disrupt the network.

- If additional info like timing or block data is emitted, looking for patterns to try and link wallets.

## Proof of Concept

**[Proxy deployment event](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L32)**:

```solidity
// Line 32
event ProxyDeployed(address indexed delegate, address proxyAddress);
```

**[This is emitted in the _deployProxyDelegatorIfNeeded function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L187)**:

```solidity
// Line 187
emit ProxyDeployed(delegate, proxyAddress);
```

**[Delegation transfer event](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L33-L37)**: 

```solidity
// Line 33 - 36   
event DelegationProcessed(
    address indexed from,
    address indexed to,
    uint256 amount
);
```

**[This is emitted in the _processDelegation function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L124-L137)**:

```solidity
// Line 136
emit DelegationProcessed(source, target, amount); 
```

The main risks come from:

- Spamming the `ProxyDeployed` event with fake addresses

- Emitting fake `DelegationProcessed` events to falsify balances

- Putting `unvalidated` data into the events t
DOS attack possible on ```delegateMulti``` function
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L57-L108


# Vulnerability details

## Summary
The ```delegateMulti``` function in ```ERC20MultiDelegate.sol``` is vulnerable to a Denial of Service (DOS) attack. A malicious actor can exploit this vulnerability by sending very large arrays, potentially disrupting the contract's operations.

## Impact
A large enough array can make it so that the amount of gas required to loop through the array exceeds the upper limit of the block gas amount.
Flooding the contract with many disproportianately large arrays can render the smart contract inoperable.
The scale of the attack could also cause broader network congestion issues.

## Proof of Concept
The vulnerability arises from a lack of checks and controls on the arrays which input to the ```delegateMulti``` function ```targets``` array and the ```amounts```

two primary oversights in the contract:

No Maximum Limit: The contract does not enforce a maximum limit on the number of elements in an array. This allows an attacker to send extremely large arrays to the delegateMulti function.
No Minimum Value Check: The contract lacks a check for minimum values in the amounts array.

## Tools Used
Manual Review
Hardhat Testing

## Recommended Mitigation Steps
Add either a minimum value check on elements in the ```amounts``` array, a maximum limit on the number of elements allowed in the ```amounts``` array or both.





## Assessed type

DoS