xanoutas/solidity-security-findings huggingface dataset:
MultisigBase.sol : Unsafe `onlySigners` modifier
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/auth/MultisigBase.sol#L44-L77
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/governance/Multisig.sol#L30-L36
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/governance/AxelarServiceGovernance.sol#L52


# Vulnerability details

## Impact

The multi sig based implementation will not serve its purpose. Proposals can be executed even by a single signer. 

## Proof of Concept
Contract has the `MultisigBase` implementation which will be used as a custom multisignature wallet where transactions must be confirmed by a threshold of signers. Where the signers and threshold may be updated every `epoch`

when we look at the [onlySigners](https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/auth/MultisigBase.sol#L44C14-L77) implementation, it return when there are not enough signed threshold met.
But the functions where this modifier is used will continued executing. 

```solidity
    modifier onlySigners() {
        if (!signers.isSigner[msg.sender]) revert NotSigner();


        bytes32 topic = keccak256(msg.data);
        Voting storage voting = votingPerTopic[signerEpoch][topic];


        // Check that signer has not voted, then record that they have voted.
        if (voting.hasVoted[msg.sender]) revert AlreadyVoted();


        voting.hasVoted[msg.sender] = true;


        // Determine the new vote count.
        uint256 voteCount = voting.voteCount + 1;


        // Do not proceed with operation execution if insufficient votes.
        if (voteCount < signers.threshold) { ----------------------------------->> threshold not met.
            // Save updated vote count.
            voting.voteCount = voteCount;
            return; ------------------------>> returned. this would allow the continuation of function execution
        }


        // Clear vote count and voted booleans.
        voting.voteCount = 0;


        uint256 count = signers.accounts.length;


        for (uint256 i; i < count; ++i) {
            voting.hasVoted[signers.accounts[i]] = false;
        }


        emit MultisigOperationExecuted(topic);


        _;
    }
```

## Tools Used

Manual review.

## Recommended Mitigation Steps

Add, separate function to vote by the valid signers.
Once the enough signer threshold met for a particular proposal, then only allow function execution ( proposal execution)


## Assessed type

Error
Multisig's functionality is impaired when Signers#threshold is 1
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/governance/Multisig.sol#L30-L36
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/auth/MultisigBase.sol#L44-L77
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/auth/MultisigBase.sol#L159-L161
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/util/Caller.sol#L18


# Vulnerability details

## Impact
Multisig's functionality is impaired when Signers#threshold is 1.
Imagine the Signers#threshold is 1, so an individual signer is possible claim all the funds from Multisig without the need for other signers to vote.
It is logical that at least 2 people should vote, but this is forgotten in MultisigBase contract.
I'm gonna explain it in the following scenario:
1. Imagine Signers#threshold is 1 and Multisig has 1 ether balance
2. A signer calls the below function (for the first argument he enters his own contract address which supports receiving ether, for second arg he enters a payable function selector which exists in his contract or leaves it empty because his contract has a payable fallback function, and for third arg he enters the Multisig contract balance or an amount of ether):
```solidity
 function execute(
        address target,
        bytes calldata callData,
        uint256 nativeValue
    ) external payable onlySigners {
        _call(target, callData, nativeValue);
    }
```
3. Contract enters to `OnlySigners` and all the checks will be passed (Remember that the threshold is set to 1)
4. Contract enters to `_call` function which checks there is enough balance and then it transfers the funds.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Edit the following line in MultisigBase#_rotateSigners:
```solidity
if (newThreshold < 2) revert InvalidSignerThreshold();
```


## Assessed type

Invalid Validation
Unauthorized Contract Upgrade Vulnerability in Upgradable Contract
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L57


# Vulnerability details

In the "upgrade" function, the contract allows the owner to upgrade the contract to a new implementation using the provided address and code hash. However, there is a flaw in the implementation that can allow an attacker to bypass the contract upgrade checks and perform unauthorized upgrades.

The issue arises from the following code block in the "upgrade" function:

if (IUpgradable(newImplementation).contractId() != IUpgradable(this).contractId()) revert InvalidImplementation();
if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();


The problem lies in the usage of the IUpgradable interface to check the contract identity and code hash of the new implementation. The IUpgradable interface does not provide an accurate way to verify contract identity, and it's susceptible to manipulation by attackers.

## Impact
The impact of this bug is significant and can lead to severe consequences for the affected smart contract and its users. Since the "Upgradable" contract allows an owner to upgrade the contract's implementation to a new contract address, the unauthorized contract upgrade vulnerability exposes the system to potential exploitation by malicious actors.

Unauthorized Control: An attacker can take control of the "Upgradable" contract by performing an unauthorized upgrade to a malicious contract of their choice. Once the attacker gains control, they can manipulate the contract's behavior, potentially leading to theft of funds or manipulation of contract logic.

Loss of User Funds: If the "Upgradable" contract manages user funds or assets, an unauthorized upgrade could lead to the loss of these funds, leaving users with no recourse to recover their assets.

Data Manipulation: The attacker can manipulate or corrupt the contract's data, leading to inconsistencies in the system and loss of integrity.

Denial of Service (DoS) Attacks: An attacker can perform malicious upgrades that disrupt the normal functioning of the contract or render it unusable, causing a denial of service for legitimate users.
## Proof of Concept
Vulnerable Contract: Upgradable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IUpgradable } from '../interfaces/IUpgradable.sol';
import { Ownable } from '../util/Ownable.sol';

/**
 * @title Upgradable Contract
 * @notice This contract provides an interface for upgradable smart contracts and includes the functionality to perform upgrades.
 */
abstract contract Upgradable is Ownable, IUpgradable {
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    address internal immutable implementationAddress;

    constructor() {
        implementationAddress = address(this);
    }

    modifier onlyProxy() {
        if (address(this) == implementationAddress) revert NotProxy(
[H] Address collisions due to lack of check on the salt parameter
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/gmp-sdk/deploy/ConstAddressDeployer.sol#L24
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/gmp-sdk/deploy/ConstAddressDeployer.sol#L42


# Vulnerability details

## Impact
In ConstAddressDeployer, functions ```deploy``` and ```deployAndInit``` do not check for the same salt value being passed in by different users. This can lead to address collisions.

If someone passes the same salt value as someone else, even with different bytecode params, the contract will return the same address for both users, but the first user will have his deployment overwritten by the second user.
## Proof of Concept

A user calls the deploy function with a specific salt and deploys their contract at address A.

Before the user's transaction is waiting to be confirmed, an attacker quickly send a transaction with the same salt value, deploying their malicious contract at the same address A.

The attacker's transaction is confirmed before the user's transaction, and their malicious contract is deployed at address A, effectively overwriting the user's intended contract.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Use a mapping that updates all the salt values that have been used to deploy a contract.
```
    mapping(bytes32 => bool) public usedSalts;
```

and pass a require statement to the deploy and deployAndInit functions
```
        bytes32 saltHash = keccak256(abi.encode(msg.sender, salt));
        require(!usedSalts[saltHash], "Salt already used");
```

OR follow the steps implemented in the ```Create3Deployer``` regarding verification of the salt value.



## Assessed type

Other
Users can abuse multicall feature on  InterchainTokenService to steal contract funds
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/Multicall.sol#L22-L25


# Vulnerability details

## Impact
Users can steal balance in InterchainTokenService to pay gas fees for remote chain calls through `multicall()` in InterchainTokenService.sol.

## Proof of Concept
User can send multiple calls at the same time on InterchainTokenService contract with the help of `multicall()` on the inherited contract Multicall.sol. However, the current implementations of `multicall()` allow users to send 0 as `msg.value` but steal balance from InterchainTokenService contract to pay for cross-chain gas fees.

In `multicall()`, there is no check on (1) whether there is cross-chain requests in `data` (2) if there is cross-chain requests in `data`, whether there is any `msg.value` or sufficient `msg.value` sent with the transaction to cover cross-chain gas fees. 

```solidity
//Multicall.sol
    function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; ++i) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                revert(string(result));
            }

            results[i] = result;
        }
    }
```
(https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/Multicall.sol#L22-L25)

As seen above, `delegateCall()` is executed without passing `msg.value`. And there is also no check on whether there is any native tokens sent with the transaction by users either. The transaction relies on users' honesty to send 'msg.value' to cover any cross-chain gas fees that are required, if there is at least one cross-chain function to be called in `multicall()`.

As an example, suppose `deployRemoteCustomTokenManager()` is part of the `data` passed to `multicall()`. `deployRemoteCustomTokenManager()` will directly use `gasValue` arg which is part of calldata user specified to pay for gas. In `_deployRemoteTokenManager()`, `gasValue` is passed to `_callContract()` to pay cross-chain gas to GasService contract. To this point, there is no check on whether there is 'msg.value' sent by the user and the `gasValue` paid could be from the InterchainTokenService contract balance.

```solidity
//InterchainTokenService.sol
 function deployRemoteCustomTokenManager(
        bytes32 salt,
        string calldata destinationChain,
        TokenManagerType tokenManagerType,
        bytes calldata params,
        uint256 gasValue
    ) external payable notPaused returns (bytes32 tokenId) {
        address deployer_ = msg.sender;
        tokenId = getCustomTokenId(deployer_, salt);
        _deployRemoteTokenManager(tokenId, destinationChain, gasValue, tokenManagerType, params);
        emit CustomTokenIdClaimed(tokenId, deployer_, salt);
    }
```
```solidity
//Inter
Reentrant Call Vulnerability in ExpressCallHandler Contract
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L79


# Vulnerability details

##DISCRIPTION
The ExpressCallHandler contract contains a potential bug related to handling the express calls. The issue lies in the _setExpressReceiveToken and _setExpressReceiveTokenWithData functions. Specifically, these functions don't properly handle reentrant calls, which could lead to unexpected behavior and potentially result in loss of funds or tokens.

The _setExpressReceiveToken and _setExpressReceiveTokenWithData functions should always check whether the prevExpressCaller is equal to address(0) (i.e., no previous express caller), and only then proceed with storing the new expressCaller. If a non-zero prevExpressCaller is detected, the function should revert with the AlreadyExpressCalled error to prevent reentrant calls and ensure that a token transfer can't be executed multiple times.

## Impact
Impact of the Bug:
The reentrant call vulnerability in the ExpressCallHandler contract can have several significant impacts on the application utilizing this contract:

Token Loss: An attacker can exploit the reentrant call vulnerability to repeatedly execute the _setExpressReceiveToken or _setExpressReceiveTokenWithData functions, causing multiple token transfers. This can lead to a loss of tokens from the contract, as they may be transferred to the recipient multiple times.

Denial of Service (DoS): Malicious actors can abuse the vulnerability to revert the token transfer process by repeatedly calling the functions with the same parameters. This can result in a DoS attack, preventing legitimate users from executing token transfers.

Smart Contract Function Misuse: The vulnerability may result in smart contract functions being called multiple times unintentionally, leading to unexpected behavior and possible state inconsistencies.

## Proof of Concept
To demonstrate the bug in the ExpressCallHandler contract, we will create a simple test scenario that triggers the reentrant call issue by manipulating the contract state.


 - Code for Exploiting the Bug:

Let's create a contract that interacts with the ExpressCallHandler contract and manipulates the state to trigger the reentrant call issue.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ExpressCallHandler.sol";

contract ExploitContract {
    ExpressCallHandler public expressCallHandler;

    constructor(address _expressCallHandler) {
        expressCallHandler = ExpressCallHandler(_expressCallHandler);
    }

    // Function to exploit the reentrant call issue
    function exploitReentrantCall() external {
        bytes32 tokenId = bytes32(0x01);
        address destinationAddress = address(0x123);
        uint256 amount = 100;
        bytes32 commandId = bytes32(0xabc);
        address maliciousCaller = address(this); // The contract itself will be the malicious caller

        // Manipulate the state to set a non-zero prevExpr
[H] Users can lose their funds if the transaction reverts on destination chain
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L502


# Vulnerability details

## Impact
The ```transmitSendToken``` function in the contract facilitates cross-chain token transfers.

For contracts involving ERC20 tokens it is also expected that, should a contract call fail, one's tokens are not lost/transferred elsewhere.

However, the function does not have a mechanism in place to handle gas refunds if the cross-chain transaction reverts on the destination chain.

## Proof of Concept
The function initiates a cross-chain transaction by calling ```_callContract```, 
which in turn uses ```gasService.payNativeGasForContractCall``` to handle gas payment for the transaction 
on the destination chain. However, there doesn't seem to be a mechanism in place to handle 
gas refunds if the cross-chain transaction reverts on the destination chain. 
As a result, if the transaction fails or reverts, the user's funds sent as gas may be lost, 
and there is no automatic refund.

## Tools Used
Manual Review
## Recommended Mitigation Steps
I would recommend implementing a gas refund mechanism to refund the unused gas to the user.



## Assessed type

Other
Potential Reentrancy in onlyAdmin modifier
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AdminMultisigBase.sol#L29


# Vulnerability details

The vulnerability lies in the `AdminMultisigBase` contract, in the `onlyAdmin` modifier. The `onlyAdmin` modifier does not prevent reentrancy, which can lead to a potential reentrancy vulnerability.

The vulnerable part of the code:

```solidity
modifier onlyAdmin() {
    uint256 adminEpoch = _adminEpoch();

    if (!_isAdmin(adminEpoch, msg.sender)) revert NotAdmin();

    bytes32 topic = keccak256(msg.data);

    // Check that admin has not voted, then record that they have voted.
    if (_hasVoted(adminEpoch, topic, msg.sender)) revert AlreadyVoted();

    _setHasVoted(adminEpoch, topic, msg.sender, true);

    // Determine the new vote count and update it.
    uint256 adminVoteCount = _getVoteCount(adminEpoch, topic) + uint256(1);
    _setVoteCount(adminEpoch, topic, adminVoteCount);

    // Do not proceed with operation execution if insufficient votes.
    if (adminVoteCount < _getAdminThreshold(adminEpoch)) return;

    _;

    // Clear vote count and voted booleans.
    _setVoteCount(adminEpoch, topic, uint256(0));

    uint256 adminCount = _getAdminCount(adminEpoch);

    for (uint256 i; i < adminCount; ++i) {
        _setHasVoted(adminEpoch, topic, _getAdmin(adminEpoch, i), false);
    }
}
```

Explanation :

1. The `onlyAdmin` modifier checks if the caller (`msg.sender`) is an admin for the current `adminEpoch`.
2. It then records that the admin has voted and increments the vote count for the specified topic.
3. If the vote count reaches the admin threshold, the modifier executes the main function body indicated by `_;`.
4. After executing the main function body, it clears the vote count and voted status for all admins.

The issue here is that if the main function body contains any external calls to untrusted contracts, it opens the possibility of reentrancy attacks. An attacker can call the main function multiple times, and each time the function reverts, it will not reset the `hasVoted` status. This allows an attacker to reenter the function and potentially execute malicious code multiple times.

To fix the reentrancy vulnerability, you should add a reentrancy guard at the beginning of the `onlyAdmin` modifier, preventing multiple calls from the same address while the function is still in execution.

Reentrancy guard:

```solidity
bool private _locked;

modifier noReentrancy() {
    require(!_locked, "Reentrant call");
    _locked = true;
    _;
    _locked = false;
}

modifier onlyAdmin() {
    uint256 adminEpoch = _adminEpoch();

    if (!_isAdmin(adminEpoch, msg.sender)) revert NotAdmin();

    bytes32 topic = keccak256(msg.data);

    // Check that admin has not voted, then record that they have voted.
    if (_hasVoted(adminEpoch, topic, msg.sender)) revert AlreadyVoted();

    _setHasVoted(adminEpoch, topic, msg.sender, true);

    // Determine the new vote count and update it.
    uint256 ad
InterchainTokenService.expressReceiveTokenWithData does not apply Check-Effect-Interaction mode
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L484-L486


# Vulnerability details

## Impact

[InterchainTokenService.expressReceiveTokenWithData](https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L467-L487) does not apply the Check-Effect-Interaction pattern. **In some edge cases, the caller can lose funds**.

## Proof of Concept

The life cycle of sending token across chains contains 3 phases:

```flow
//P1: launch on source chain
TokenManager.callContractWithInterchainToken
  interchainTokenService.transmitSendToken
    interchainTokenService._callContract
      gateway.callContract
        emit ContractCall
        
//P2: The off-chain program collects all events of the target chain, 
//builds the Command array, and operators sign for them. 
//Call AxelarGateway.execute of the target chain to set each command to be executed to true.
AxelarGateway.execute
  IAxelarAuth(AUTH_MODULE).validateProof
  //Loop through setting each command to true
  _setCommandExecuted(commandId, true);
  _setCommandExecuted(commandId, true);
  ......
  
//P3: The relayer calls InterchainTokenService.execute for each command, 
//or it can be called by the user himself.
InterchainTokenService.execute  //we only consider SELECTOR_SEND_TOKEN_WITH_DATA here.
  gateway.validateContractCall
  InterchainTokenService._execute
    _processSendTokenWithDataPayload
      _popExpressReceiveTokenWithData	
      tokenManager.giveToken
      destinationAddress.executeWithInterchainToken
```

expressReceiveTokenWithData uses the caller's tokens to fullfill a callContractWithInterchainToken ahead of time. **It is only called before the command's execution status was set to true, due to [this check](https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L476)**. That is, before the completion of P2 in the above life cycle. It first transfers the token from the caller to `destinationAddress`, then calls [destinationAddress.expressExecuteWithInterchainToken](https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L484), and finally calls [_setExpressReceiveTokenWithData](https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L135) to set the value in the corresponding slot to the caller address(called expressCaller). **Such a process is actually the Check-Interaction-Effect mode**. `destinationAddress` can be a malicious contract, and it is very dangerous to interact with the malicious contract before setting the slot.

Suppose the following scenario:

1.  Bob deploys a malicious contract A and calls `TokenManager.callContractWithInterchainToken` to send 100e18 token.
    
2.  Alice's program periodically (every 1 minute) checks whether there is an outgoin