xanoutas/solidity-security-findings huggingface dataset:
calling the `sendToken` by the `TokenManagerProxy` proxy will fail because  of the `onlyTokenManager` modifier
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/proxies/TokenManagerProxy.sol#L69-L89
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/token-manager/TokenManager.sol#L92
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L509
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L128-L136


# Vulnerability details

## Impact

the function `sendToken` in `TokenManger.sol` won't work as expected when it called by proxy `TokenManagerProxy.sol` which it used to delegate users call to the `TokenManger` contract to execute the functions, the `sendToken` will always revert if the call made by EOA address which make a call to the proxy to delegate the call to the implementation logic contract, because of the `interchainTokenService.transmitSendToken` all delegate calls will revert and make the proxy contract useless. more details in POC

## Proof of Concept

the function `setToken` make a call to the `transmitSendToken` to send the msg.value and the call data:

```solidity
function sendToken(
    string calldata destinationChain,
    bytes calldata destinationAddress,
    uint256 amount,
    bytes calldata metadata
) external payable virtual {
    //set owner
    address sender = msg.sender;
    // send the amount to this contract
    amount = _takeToken(sender, amount);
    // add the flow out in a slot
    _addFlowOut(amount);
    // make the transfer of the token
    //@audit
    interchainTokenService.transmitSendToken{ value: msg.value }(
        //amount of msg.value may decrease
        _getTokenId(),
        sender,
        destinationChain,
        destinationAddress,
        amount,
        metadata
    );
}

```

this function will make a call to the `transmitSendToken` which have the `onlyTokenManager` modifier that check if the msg.sender is the tokenManger contract address:

```solidity
function transmitSendToken(
        bytes32 tokenId,
        address sourceAddress,
        string calldata destinationChain,
        bytes memory destinationAddress,
        uint256 amount,
        bytes calldata metadata
    ) external payable onlyTokenManager(tokenId) notPaused {

        ...
        // we don't need the function logic for this finding
        ...
    }
```

as we see the `onlyTokenManager` is used to prevent this function to be called by EOA or any contract address, but this will even prevent users to call the `sendToken` by the proxy because the proxy will set the caller to EOA for both contract `TokenManger.sol` and `InterChainService.sol` and this case the delegate call by the proxy will revert when it reach the `transmitSendToken` function, the flow is like this :

EOA/external contract --call--> Toke
Malicious callers can get all the token to be sent to destinationAddress by only sending a dust amount to destinationAddress
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#L439-L451
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L613
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/ExpressCallHandler.sol#L92-L94


# Vulnerability details

## Impact
A malicious caller can get all the tokens to be sent to the destination address by calling `expressReceiveToken()` on InterchainTokenService.sol and only send a dust amount to the destination address. The actual receiver(destination address) will lose the funds they are supposed to receive.

## Proof of Concept
When an interchain token transfer request is emitted and before it was executed on InterchainTokenService, Axelar provides a feature to allow any caller to send the tokens transmitted to the destination address first through `expressReceiveToken()`, and such caller will be reimbursed when the request is later executed which is the intended behavior.

However, this process can be abused by a malicious caller sending only a dust amount which is enabled by the `expressReceiveToken()` not checking or recording the `amount` caller is sending.

```solidity
//InterchainTokenService.sol 
    function expressReceiveToken(bytes32 tokenId, address destinationAddress, uint256 amount, bytes32 commandId) external {
        if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

        address caller = msg.sender;
        ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
        IERC20 token = IERC20(tokenManager.tokenAddress());

        SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

        _setExpressReceiveToken(tokenId, destinationAddress, amount, commandId, caller);
    }
```
(https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L439-L451)

As seen above, a caller can input any `amount` to send to `destinationAddress`. In `_setExpressReceiveToken()`, the `amount` is not recorded on chain and only `expressCaller` is stored.

```solidity
//ExpressCallHandler.sol - _setExpressReceiveToken()
...
        assembly {
            sstore(slot, expressCaller)
        }
 emit ExpressReceive(tokenId, destinationAddress, amount, commandId, expressCaller);
...
```
(https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/ExpressCallHandler.sol#L92-L94)

When this request is finally executed on InterchainTokenService.sol through `execute()`-> `_execute()`, the internal `_processSendTokenPayload()` is invoked. This function check whether there is an existing `expressCaller` in storage, if yes, the total
users may send tokens and data to empty destination chain address and loosing all their tokens and data
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#L713
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L133-L138
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L95-L99


# Vulnerability details

## Impact

users may send token to untrusted or un available token Manger on specific chain when calling `_callContract` function, this function return the destination chain by calling the `getRemoteAddress` function in the `RemoteAddressValidator.sol` which it return the valid address for specific chain using the chain name, if the remoteAddress is removed by the `removeTrustedAddress` function then the users will send tokens and data to invalid destination chain(empty) and cause their transactions data and amount lost forever. more details in POC

## Proof of Concept

let's say Alice make a call to a function that calling the `_callContract` function(this function is internal) and she set the all params and the destination chain name(any evm-compatible chain), the `_callContract` is implemented like this:

```solidity
function _callContract(
    string calldata destinationChain, //the name of the target chain
    bytes memory payload,
    uint256 gasValue,
    address refundTo
) internal {
    //@audit
    string memory destinationAddress = remoteAddressValidator.getRemoteAddress(destinationChain);
    if (gasValue > 0) {
        gasService.payNativeGasForContractCall{ value: gasValue }(address(this), destinationChain, destinationAddress, payload, refundTo);
    }
    gateway.callContract(destinationChain, destinationAddress, payload);
}

```

as we see the `destinationAddress` will be get by calling the `remoteAddressValidator.getRemoteAddress` function which implemented like that:

```solidity
function getRemoteAddress(string calldata chainName) external view returns (string memory remoteAddress) {
    //@audit
    remoteAddress = remoteAddresses[chainName];
    if (bytes(remoteAddress).length == 0) {
        remoteAddress = interchainTokenServiceAddress.toString();
    }
}

```

the code above look like it work as expected but the problem will accure when Alice(users) make a call and setting an destination chain that is removed by the owner which the owner make a call to remove the `removeTrustedAddress` function :

```solidity
function removeTrustedAddress(string calldata chain) external onlyOwner {
    // this function only set data to zero and empty not decreasing the length of the mapping or set it to zero !
    // did this remove correctly!
    if (bytes(chain).length == 0) revert ZeroStringLength();
    remoteAddressHashes[chain] = bytes32(0);
    remoteAddresses[chain] = '';
    emit TrustedAddressRemoved(c
It is possible to deploy a TokenManager for a canonical token that already has one
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#L309-L314
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L808-L820
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/TokenManagerDeployer.sol#L33-L43


# Vulnerability details

## Impact
When trying to register a token as a canonical token, user has to call registerCanonicalToken() in the InterchainTokenService contract which then calls _deployTokenManager() in the same contract which then make a delegate call to the deployTokenManager() function in TokenManagerDeployer.

In registerCanonicalToken() a check is there on L311 to make sure the gateway has not already registered this token with the same address:

`if (gateway.tokenAddresses(tokenSymbol) == tokenAddress) revert GatewayToken();`

However it is possible to directly call deployTokenManager() as it is declared as an external function without going through this check, which would lead to a situation where the same token has 2 TokenManager deployed:
`
    function deployTokenManager(bytes32 tokenId, uint256 implementationType, bytes calldata params) external payable {
        bytes memory args = abi.encode(address(this), implementationType, tokenId, params);
        bytes memory bytecode = abi.encodePacked(type(TokenManagerProxy).creationCode, args);
        address tokenManagerAddress = deployer.deploy(bytecode, tokenId);
        if (tokenManagerAddress.code.length == 0) revert TokenManagerDeploymentFailed();
    }
}
`

## Recommended Mitigation Steps

Move the check from L311 to the deployTokenManager() function in TokenManagerDeployer.





## Assessed type

Access Control
All approved proposals will not be executed if rotate signers called before
Audit: 2023-07-axelar-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/AxelarServiceGovernance.sol#L48 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L44 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L142


# Vulnerability details

## Impact
`executeMultisigProposal` function is connected with the modifier `onlySigners` which counts a vote when any valid signer calls this function. And this modifier checks whether the votes threshold meat or not, if not then count a vote and return;

```solidity
// Do not proceed with operation execution if there are insufficient votes.
if (voteCount < signers.threshold) {
    // Save updated vote count.
    voting.voteCount = voteCount;
    return;
}
```

There is also another function `rotateSigners` in the `MultisigBase` which is also controlled by `onlySigners` modifier, this function takes new accounts and new votes threshold and updates them, and increases a very special variable `++signerEpoch;`.  

Now suppose the `AxelarServiceGovernance` collected and approved many multi-sig proposals (some also send native coin value) and some of the signers executed them which counts their vote for that proposal (but still these proposal needs votes to reach the threshold in order to execute).  

In between, in the `Multisig` contract signers' called the `rotateSigners` function (which also contains the old signers as well).

Now if the old signers call the `executeMultisigProposal` for the proposals which were approved before updating the accounts array their votes will not be able to count for those proposals and due to not reaching the threshold value they will not be executed. 

The only solution is to find all of the signers who had given the votes for these proposals and tell them to cast their votes for these proposals again. And surprisingly there is no way to know which signers had given votes for old topics because `hasSignerVoted` for a particular topic does not tell about old topics.

This will waste all of the time and execution costs of `AxelarServiceGovernance` and other signers who spent on scheduling, approval and execution of proposals (stuck native coins value can be recovered using new proposals by signers).

See the POC for details explaination.


## Proof of Concept

Call this inside `AxelarServiceGovernance.js` file.

```js
it.only("should execute a multisig proposal", async () => {
    // * this is the command id required for multi-sig proposal approval.
    const commandID = 2;
    const target = targetContract.address;
    const nativeValue = 10;

    // * this is the calldata required to call the function when proposal reached the threshold
    const targetInterface = new Interface(["function callTarget() external"]);
    const calldata = targetInterface.encodeFunctionData("callTarget");

    // * proposal hash for events checking.
    const proposalHash = solidityKeccak256(
newer tx can remain un approved due nto previous tx not passing 
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/governance/Multisig.sol#L20


# Vulnerability details

## Impact
Newer transaction will not get approved because previous on es have not been approved or rejected 

## Proof of Concept
Let's say two out of 3 addresses sign a transaction which requires 3 signers then after some time the 3 now sign another transaction it wont get approved by validators unless the previous ones get validated or rejected, because of nonce in the evm. 

## Tools Used
Manual review 
## Recommended Mitigation Steps
There should be a time frame that sets a pending transaction to reject automatically if it has not been validated.


## Assessed type

DoS
Accepted proposal may be recreated at the same address with a malicious proposal if there's a self destruct function in the accepted proposal
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/cgp/governance/InterchainGovernance.sol#L68-L79


# Vulnerability details

## Impact

A malicious proposal can take over the contract address of the accepted proposal through self-destruct

## Proof of Concept

This issue is regarding the Tornado cash hack, whereby the attacker deploys different contracts at the same address. If the proposal manages to have a self-destruct function inside the code, then they can destroy their proposed contract and create a malicious one with the same address. 


Tornado Cash Hack - https://www.youtube.com/watch?v=whjRc4H-rAc&t=362s

```
    function _processCommand(
        uint256 commandId,
        address target,
        bytes memory callData,
        uint256 nativeValue,
        uint256 eta
    ) internal override {
        if (commandId > uint256(type(ServiceGovernanceCommand).max)) {
            revert InvalidCommand();
        }


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


        if (command == ServiceGovernanceCommand.ScheduleTimeLockProposal) {
            eta = _scheduleTimeLock(proposalHash, eta);


            emit ProposalScheduled(proposalHash, target, callData, nativeValue, eta);
            return;
        } else if (command == ServiceGovernanceCommand.CancelTimeLockProposal) {
            _cancelTimeLock(proposalHash);


            emit ProposalCancelled(proposalHash, target, callData, nativeValue, eta);
            return;
        } else if (command == ServiceGovernanceCommand.ApproveMultisigProposal) {
            multisigApprovals[proposalHash] = true;
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

Not sure about the recommendation, but just be alert to check for the self-destruct function in all the proposals that come in


## Assessed type

Other
ProposalHash can be easily duplicated with same target, callData and nativeValue
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
```solidity
    function executeMultisigProposal(
        address target,
        bytes calldata callData,
        uint256 nativeValue
    ) external payable onlySigners {
        bytes32 proposalHash = keccak256(abi.encodePacked(target, callData, nativeValue));

        if (!multisigApprovals[proposalHash]) revert NotApproved();

        multisigApprovals[proposalHash] = false;

        _call(target, callData, nativeValue);

        emit MultisigExecuted(proposalHash, target, callData, nativeValue);
    }
```
`proposalHash` is calculated from target, callData, and nativeValue.
These values can be same if the signer wants to call the same function with same data and value.
Using this kind of hash prevents such operations.

## Proof of Concept
PoC is straightforward.

## Tools Used
Manual

## Recommended Mitigation Steps
Add `block.timestamp` or salt to hash. Possible to use incremental ID too.








## Assessed type

call/delegatecall
A reorg on the origin chain after a proposal has been validated will result in loss of funds for the protocol
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L88


# Vulnerability details

## Impact
The protocol will lose the funds that the user sent to the origin chain InterchainProposalSender contract for the proposal.

## Proof of Concept
When a proposal gets submitted it gets reviewed by validators on the Axelar network and then receives a validation on the destination chain so it can get executed by the execution contract there.
The issue here arises because of chain reorgs, which happen on certain chains relatively frequently. For example, Polygon has multiple chain reorgs daily, and most of them have a depth of more than 1 block. One of the longest block reorgs that happened recently on Polygon even had a depth of 157 blocks(6 minutes). https://forum.polygon.technology/t/157-block-reorg-at-block-height-39599624/11388
https://polygonscan.com/blocks_forked

The validation of transactions with Polygon as an origin chain takes 6-7 minutes on average. This is just over the amount of time the reorg mentioned above took.

Take the following scenario into account:
1. Bob creates a proposal on Polygon to Mainnet that sends 1 ETH to an arbitrary amount and has a stipend of 1 million gas. This comes out to around 1.1 ETH in value.
2. 6 minutes and 20 seconds pass and Bob's transaction gets validated on the Axelar network and also gets approved on the destination chain's executor contract. A relayer picks it up and executes it successfully.
3. Polygon reorgs with a depth of 170 blocks, which is just above the time it took the transaction to get executed. As a result, all of the funds Bob sent on Polygon are still in his wallet.

## Tools Used
Manual Review

## Recommended Mitigation Steps
I am not exactly sure what can be done to prevent it on the protocol's side other than increasing the validation wait times further.


## Assessed type

Other
Users who call `expressReceiveTokenWithData` or `expressReceiveToken` can griefed to pay for fees
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L439-L454
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L467-L487


# Vulnerability details

## Impact

A user or service who calls `expressReceiveTokenWithData` or `expressReceiveToken` generally waits for an event associated with either `callContractWithInterchainToken` or `sendToken` to be emitted on a second chain. When they see this event, they will call `expressReceiveTokenWithData` or `expressReceiveToken`, and then wait until the token transfer crosses to their chain to receive back their funds. The issue is that the caller that triggers this event on the second chain can simply pay 1 wei of gas in the call to `gasService.payNativeGasForContractCall{ value: gasValue }(..)`, which will allow any relevant events to be emitted for the transfer, but will not be enough to actually transfer the tokens across chain.

Back on the first chain, the `destinationAddress` will have received the tokens from the user. However, this user who had called `expressReceiveTokenWithData` or `expressReceiveToken` will be stuck in a state in which they will not receive their tokens back, unless they pay the fee in native tokens to the AxelarGasService to get that transaction to cross chains. In this way, the malicious user on the second chain can grief the user/service into paying for the fees of the message.

## Proof of Concept

Steps:
1. malicious user triggers `callContractWithInterchainToken` or `sendToken` on source chain using 1 wei of fees
2. normal user or service on destination chain calls `expressReceiveTokenWithData` or `expressReceiveToken`. At this point, the `destinationAddress` of the malicious user will have already received their tokens
3. the normal user or service on the destination chain will then be required to pay the remaining fees to get the token transfer in step 1 to go cross-chain, else they will lose funds

## Tools Used

Manual review

## Recommended Mitigation Steps

Add a recovery mechanism which allows the caller of `expressReceiveTokenWithData` or `expressReceiveToken` to receive their funds back, or somehow check that the gas paid to the AxelarGasService for a given commandId is enough


## Assessed type

Other