xanoutas/solidity-security-findings huggingface dataset:
MID-Risk Vulnerabilities in the Axelar Smart Contracts
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/governance/AxelarServiceGovernance.sol#L73


# Vulnerability details

## Impact

The vulnerabilities that I have identified could have a significant impact on the Axelar network. These vulnerabilities could be exploited by an attacker to:

Gain control of the Axelar network by proposing and voting on malicious proposals.
Mint or burn tokens in an unauthorized manner, which could have a significant impact on the value of the token.
Replace the TokenManager contract with a malicious contract, which would allow them to mint or burn tokens in an unauthorized manner.
## Proof of Concept

The proof of concept for each vulnerability is as follows:

AxelarServiceGovernance.sol
The contract does not have any checks to prevent an attacker from proposing a malicious proposal. This can be demonstrated by proposing a malicious proposal that would give the attacker control of the Axelar network.
The contract does not have any checks to prevent an attacker from voting on a malicious proposal. This can be demonstrated by voting on a malicious proposal that would give the attacker control of the Axelar network.
MultisigBase.sol
The contract does not have any checks to prevent an attacker from adding malicious signers to the multisig. This can be demonstrated by adding a malicious signer to the multisig, giving the attacker control of the multisig.
The contract does not have any checks to prevent an attacker from removing authorized signers from the multisig. This can be demonstrated by removing an authorized signer from the multisig, giving the attacker control of the multisig.
Multisig.sol
The contract does not have any checks to prevent an attacker from submitting a transaction with invalid signatures. This can be demonstrated by submitting a transaction with invalid signatures, which would be approved by the multisig.
InterchainToken.sol
The contract does not have any checks to prevent an attacker from minting or burning tokens in an unauthorized manner. This can be demonstrated by minting or burning tokens in an unauthorized manner, which could have a significant impact on the value of the token.
TokenManagerProxy.sol
The contract does not have any checks to prevent an attacker from replacing the TokenManager contract with a malicious contract. This can be demonstrated by replacing the TokenManager contract with a malicious contract, which would allow the attacker to mint or burn tokens in an unauthorized manner.
## Tools Used

The tools that I used to identify these vulnerabilities include:

Solidity
Remix IDE
Etherscan
## Recommended Mitigation Steps

The following mitigation steps can be taken to address the vulnerabilities that I have identified:

Add checks to the contracts to prevent attackers from proposing malicious proposals, voting on malicious proposals, adding malicious signers to the multisig, removing authorized signers from the multisig, submitting transaction
Gas refunds are returned to the wrong address, leading to loss of funds for the payer
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token/InterchainToken.sol#L79-L105
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-manager/TokenManager.sol#L136-L153
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L502-L523


# Vulnerability details

## Impact

To pay for cross-chain messages/calls, a user/third-party service must pay a reasonable fee (in this case using the native token). However, often times a user will overpay to ensure that there is an adequate buffer for their tx to successfully succeed in case of market turbulence. There is logic to return this excess to the user in the AxelarGasService contract. However, in order to ensure that the original caller for a tx (payer of the fees) gets their refund, their address must be correctly specified in the call to `payNativeGasForContractCall` as the `refundAddress`.

In the case where a user or third-party service is calling `interchainTransferFrom` on a token contract which inherits the logic from the InterchainContract, that user will not receive any of the refund of the overpayment of the native token that they used to pay for the tx. Instead, this refund will be sent to a different address, resulting in a direct loss of funds for the caller. This will add up, especially if transfers are being automated/run by a third-party service.

## Proof of Concept

Let's consider the entire flow of the `interchainTransferFrom` function call to see how refunds are not being sent to the correct address. The `interchainTransferFrom` function is defined as follows:
```solidity
function interchainTransferFrom(
    address sender,
    string calldata destinationChain,
    bytes calldata recipient,
    uint256 amount,
    bytes calldata metadata
) external payable {
	...
    tokenManager.transmitInterchainTransfer{ value: msg.value }(sender, destinationChain, recipient, amount, metadata);
}
```
Here the sender is the address to transfer tokens from in a standard `transferFrom` call. The `transmitInterchainTransfer` function is defined as follows:
```solidity
function transmitInterchainTransfer(
    address sender,
    string calldata destinationChain,
    bytes calldata destinationAddress,
    uint256 amount,
    bytes calldata metadata
) external payable virtual onlyToken {
    amount = _takeToken(sender, amount);
    _addFlowOut(amount);
    interchainTokenService.transmitSendToken{ value: msg.value }(
        _getTokenId(),
        sender,
        destinationChain,
        destinationAddress,
        amount,
        metadata
    );
}
```
Again, `sender` is the address from which tokens are being transferred from, rather than the address paying for the cross-chain call. In the `transmitSendToken` function:
```solidity
function transmitSendToken(
    bytes32 tokenId,
    address sourceAddress,
    string calldata destinationChain,
    bytes memory desti
InterchainProposalExecutor will fail if any proposal requires value transfer, breaking core logic
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L41-L67
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L76


# Vulnerability details

## Impact

Proposals which are sent from a source chain (using InterchainProposalSender) to a destination chain to be executed (using InterchainProposalExecutor) are intended to support function calls that include transfers of ETH, or other native token. However, the `_execute` function of the InterchainProposalExecutor contract does not accept ETH, meaning that any attempt to call a function that requires value transfer will result in the entire transaction failing. Additionally, even if a single function call triggered in a call to `_execute` requires sending ETH, the entire tx will revert, meaning none of the other function calls can be run for the proposal either.

## Proof of Concept

The `_execute` function of the InterchainProposalExecutor contract is defined as follows:
```solidity
function _execute(
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes calldata payload
) internal override {
	...
    // Decode the payload
    (address interchainProposalCaller, InterchainCalls.Call[] memory calls) = abi.decode(payload, (address, InterchainCalls.Call[]));
	...
    // Execute the proposal with the given arguments
    _executeProposal(calls);
    ...
}
```
It decodes the function calls which are required to be run for a given proposal, and these are passed to the `_executeProposal` function:
```solidity
function _executeProposal(InterchainCalls.Call[] memory calls) internal {
    for (uint256 i = 0; i < calls.length; i++) {
        InterchainCalls.Call memory call = calls[i];
        (bool success, bytes memory result) = call.target.call{ value: call.value }(call.callData);

        if (!success) {
            _onTargetExecutionFailed(call, result);
        } else {
            _onTargetExecuted(call, result);
        }
    }
}
```
The individual calls support `call.value`, but because there is no way for a user to send ETH when making this call, all calls which require value transfer will revert the entire tx.

## Tools Used

Manual review

## Recommended Mitigation Steps

Similar to how `executeProposal` is payable for the InterchainGovernance contract, there should be an 'execute' function which is payable.


## Assessed type

Payable
`newGovernance` COULD BE SET TO A WRONG ADDRESS THUS FREEZING CERTAIN FUNCTIONS
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L254-L258
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L681-L685


# Vulnerability details

## Impact

The `AxelarGateway.transferGovernance` function is used to set the `newGovernance` address in the storage of the `AxelarGateway` contract. It is performed as follows:

    function transferGovernance(address newGovernance) external override onlyGovernance {
        if (newGovernance == address(0)) revert InvalidGovernance();

        _transferGovernance(newGovernance);
    }

The `AxelarGateway._transferGovernance` function is as follows:

    function _transferGovernance(address newGovernance) internal {
        emit GovernanceTransferred(getAddress(KEY_GOVERNANCE), newGovernance);

        _setAddress(KEY_GOVERNANCE, newGovernance);
    } 

As it is seen above the `transferGovernance` can only be called by the `governance` address since it is protected by the `onlyGovernance` modifier. Hence if a wrong address is set as the `newGovernance` of the contract via the `transferGovernance` function call, the `onlyGovernance` modifier will be locked for ever since no one will be able to change the `governance` after that.

As a result the critical function `AxelarGateway.upgrade` will be locked as well since it is controlled by the `onlyGovernance` modifier.

## Proof of Concept

```solidity
    function transferGovernance(address newGovernance) external override onlyGovernance {
        if (newGovernance == address(0)) revert InvalidGovernance();

        _transferGovernance(newGovernance);
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L254-L258

```solidity
    function _transferGovernance(address newGovernance) internal {
        emit GovernanceTransferred(getAddress(KEY_GOVERNANCE), newGovernance);

        _setAddress(KEY_GOVERNANCE, newGovernance);
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L681-L685

## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Hence it is recommended to use a method similar to `openzeppelin` `two step ownership transfer` to change the `governance` address in the `AxelarGateway` contract. Where the current `governance` can propose the `newGovernance` and then the `newGovernance` should accept the role by calling the respective function as the `msg.sender`.

This will eliminate the possiblity of an errorneous address being set as the `governance` and thus locking the `onlyGovernance` modifier controlled functions permenantly.


## Assessed type

Other
A whale can DoS the token service by reaching flowlimit
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Clients can't transfer tokens between chains because the flow limit is reached.

## Proof of Concept
In FlowLimit.sol:_addFlow(), we revert if current flowToAdd + flowAmount > flowToCompare + flowLimit:

```solidity
        uint256 flowToAdd;
        uint256 flowToCompare;
        assembly {
            flowToAdd := sload(slotToAdd)
            flowToCompare := sload(slotToCompare)
        }
        if (flowToAdd + flowAmount > flowToCompare + flowLimit) revert FlowLimitExceeded(); // @audit-issue MEDIUM a whale can keep flow in or out to dos others
        assembly {
            sstore(slotToAdd, add(flowToAdd, flowAmount))
        }
    }
```

So a whale (or the liquid of tokens is low with a low limit) can keep reaching the limit, thus DoS the whole token service. The attacker doesn't lose tokens, only needs to pay the gas.

## Tools Used
Manual Review.

## Recommended Mitigation Steps
Add a flow limit to every single token transfer, making the whale attacker has to pay more gas for more transfers.


## Assessed type

DoS
Attacker can create additional canonical token bridge
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Deployers of custom `TokenManagers` can subvert the limitation imposed in the documentation that allows only one Canonical Bridges for each existing ERC20 token: “deployers can deploy a Canonical Bridge for any token they want, this can be done only once per pre-existing ERC20". 

There is no restriction on custom `TokenManagers` that restricts which token they may deploy a bridge for:

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

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol#L36-L41

This could allow a malicious deployer to create a liquidity pool `TokenManager` where the address of the liquidity pool is a honeypot contract from which they can drain funds. Once the operator has drained funds from the pool it would almost certainly ruin the operator's reputation and the bridge would probably stop being used but they could repeat this same strategy by only paying the gas fee for the deployment of a new `TokenManager` contract. 

This creates an inability to determine the source of truth as to which bridge is the true canonical bridge for an ERC20 token because each bridge only stores `implementationType` and `tokenId` in its proxy state variables:

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/proxies/TokenManagerProxy.sol#L15C5-L16

with no identifier as to which was deployed by calling the `registerCanonicalToken` function in `InterchainTokenService.sol`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement a check in the `deployCustomTokenManager` function of `InterchainTokenService.sol` that checks if a `TokenManager` exists for the given `tokenAddress`:

```solidity

function deployCustomTokenManager(
        bytes32 salt,
        TokenManagerType tokenManagerType,
        bytes memory params
    ) public payable notPaused returns (bytes32 tokenId) {
        address deployer_ = msg.sender;
        address canonicalTokenId;
        (, address tokenAddress_, ) = abi.decode(params, (bytes, address, address));
        canonicalTokenId = getCanonicalTokenId(tokenAddress_);
        try{
            getValidTokenManagerAddress(canonicalTokenId);
            revert CanonicalBridgeAlreadyExists();
        }
        catch (RevertException e) {
            tokenId = getCustomTokenId(deployer_, salt);
        
            _deployTokenManager(tokenId, tokenManagerType, params);
            emit CustomTokenIdClaimed(tokenId, deployer_, salt);
        }

```




## Assessed type

Invalid Validation
InitProxy and Proxy may revert preventing successful init
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/gmp-sdk/upgradable/InitProxy.sol#L42-L60


# Vulnerability details

## Impact
When InitProxy is constructed the owner is set to be the sender address so that the init function can only be called by the same sender address. If the init function is receiving a new owner, the implementation may revert during setup preventing proxy initialization.

Same issue is present for Proxy contract.

## Proof of Concept

At [Line 43 and 46 of InitProxy.sol](https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/gmp-sdk/upgradable/InitProxy.sol#L43-L46) the code verifies the caller is the same that constructed the proxy. At [Line 54 of InitProxy.sol](https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/gmp-sdk/upgradable/InitProxy.sol#L53-L59) the `_OWNER_SLOT`  is set to `newOwner` with the purpose to change the owner to a new account. If the implementation does have owner role checks during the setup (either on the implementation setup itself to prevent direct logic initialization by malicious users or on child calls of the setup) the transaction will revert because the `_OWNER_SLOT` will return the `newOwner` which is different from the current caller.

The following PoC code block is a copy & paste ready collection of interfaces and contracts that can be quickly executed in remix. The relevant part is at the bottom (`InitProxy` and `PocImplementation`). `PoCImplementation` has an `onlyOwer` modifer on the `setup` function (which makes sense from a security perspective) to prove the init will fail the implementation `setup` delegatecall if a `newOwner` address is specified that is different from the `InitProxy` deployer address.

```solidity
// General interface for upgradable contracts
interface IProxy {
    error InvalidOwner();
    error InvalidImplementation();
    error SetupFailed();
    error NotOwner();
    error AlreadyInitialized();

    function implementation() external view returns (address implementation_);

    function setup(bytes calldata setupParams) external;
}

// General interface for upgradable contracts
interface IInitProxy is IProxy {
    function init(
        address implementationAddress,
        address newOwner,
        bytes memory params
    ) external;
}

// General interface for upgradable contracts
interface IUpgradable {
    error NotOwner();
    error InvalidOwner();
    error InvalidCodeHash();
    error InvalidImplementation();
    error SetupFailed();
    error NotProxy();

    event Upgraded(address indexed newImplementation);
    event OwnershipTransferred(address indexed newOwner);

    // Get current owner
    function owner() external view returns (address);

    function contractId() external pure returns (bytes32);

    function implementation() external view returns (address);

    function upgrade(
sendToken() shouldn't have metadata parameter
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-manager/TokenManager.sol#L87
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L511


# Vulnerability details

## Impact
Users calling sendToken with metadata may trigger a revert or lose the calldata if its size is 1-3 bytes when the destination is an EOA.

## Proof of Concept
In transmitSendToken(), we discard the calldata if it's less than 4 bytes, and revert if it's bigger or equal to 4 bytes and the version is not zero:

```solidity
        if (metadata.length < 4) {
            payload = abi.encode(SELECTOR_SEND_TOKEN, tokenId, destinationAddress, amount);
            _callContract(destinationChain, payload, msg.value, sourceAddress);
            emit TokenSent(tokenId, destinationChain, destinationAddress, amount);
            return;
        }
        uint32 version;
        (version, metadata) = _decodeMetadata(metadata);
        if (version > 0) revert InvalidMetadataVersion(version);
```

The TokenManager.sol:sendToken() is meant to send tokens (SELECTOR_SEND_TOKEN), but it also has a metadata parameter (without natspec comment). This may lead to two impacts on users:
1. If a user tries to send tokens to an EOA with 1-3 bytes calldata (this will success since EOA will accept all calldata), then the actual transaction will not contain the calldata, which may lead to unexpected situations.
2. If a user wrongly calls sendToken() with calldata bigger than 4 bytes, his transaction will fail. This may seem confusing to users since he sees a metadata parameter.

## Tools Used
Manual Review.

## Recommended Mitigation Steps
Remove the metadata parameter of sendToken(), i.e., make sure transmitSendToken(..., metadata) the metadata is zero length, just like we ensure the version is zero in callContractWithInterchainToken().



## Assessed type

Context
`TimeLock` CANNOT SCHEDULE THE SAME PARAMETER BASED PROPOSALS MULTIPLE TIMES
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L50-L61
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L125
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L128


# Vulnerability details

## Impact

The `TimeLock._scheduleTimeLock` function is used to set a delay after which the proposal can be executed. But the `proposalHash` which is passed in as an input parameter to the `_scheduleTimeLock` function is calculated as follows:

        bytes32 proposalHash = keccak256(abi.encodePacked(target, callData, nativeValue));

Hence later, if the same set of `target`, `calldata`, `nativeValue` are used to change the `eta` of the proposal before previous timelock is over (By the governance due to reasonable requirement), the transaction will revert since it will give the same `proposalHash` and following condition in the `TimeLock._scheduleTimeLock` function will be triggered.

        if (_getTimeLockEta(hash) != 0) revert TimeLockAlreadyScheduled();

This means one cannot schedule the same transaction multiple times.

## Proof of Concept

```solidity
    function _scheduleTimeLock(bytes32 hash, uint256 eta) internal returns (uint256) {
        if (hash == 0) revert InvalidTimeLockHash();
        if (_getTimeLockEta(hash) != 0) revert TimeLockAlreadyScheduled();

        uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;

        if (eta < minimumEta) eta = minimumEta;

        _setTimeLockEta(hash, eta);

        return eta;
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L50-L61

```solidity
        bytes32 proposalHash = _getProposalHash(target, callData, nativeValue);
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L125

```solidity
            eta = _scheduleTimeLock(proposalHash, eta);
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L128

## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Hence it is recommended to include `eta` in the hash. (Compound's Timelock uses the `eta` in the hash calculation.) This way the same transaction data can be used by specifying a different `eta`.


## Assessed type

Other