Axelar Network - Chom's results

Decentralized interoperability network.

General Information

Platform: Code4rena

Start Date: 12/07/2023

Pot Size: $80,000 USDC

Total HM: 11

Participants: 47

Period: 9 days

Judge: berndartmueller

Total Solo HM: 1

Id: 260

League: ETH

Axelar Network

Findings Distribution

Researcher Performance

Rank: 7/47

Findings: 2

Award: $1,956.96

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Jeiwan

Also found by: Chom, Shubham

Labels

bug
2 (Med Risk)
satisfactory
duplicate-323

Awards

978.4802 USDC - $978.48

External Links

Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L69-L89 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L707-L724 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L133-L138

Vulnerability details

Impact

Case sensitive wallet address chains are not supported by InterchainTokenService

For instance, both P2PKH and P2SH wallet address formats are case-sensitive, so you have to make sure that you're using uppercase and lowercase letters where appropriate. An example of a case-sensitive address is 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2.

It will cause fund loss on bridging to that chain since it will always send messages to the contract address in lowercase format. But since that chain is case sensitive, the contract address in lowercase format is invalid and not found, so the token can't be unlocked on the destination chain and lost forever!

Since Axelar plans to support more chains in the future, there may be some chain whose address is case sensitive

Proof of Concept

RemoteAddressValidator validateSender and addTrustedAddress convert address to lowercase before storing or comparing. This limits the support to only case-insensitive chain

    function validateSender(string calldata sourceChain, string calldata sourceAddress) external view returns (bool) {
        string memory sourceAddressLC = _lowerCase(sourceAddress);
        bytes32 sourceAddressHash = keccak256(bytes(sourceAddressLC));
        if (sourceAddressHash == interchainTokenServiceAddressHash) {
            return true;
        }
        return sourceAddressHash == remoteAddressHashes[sourceChain];
    }

    function addTrustedAddress(string memory chain, string memory addr) public onlyOwner {
        if (bytes(chain).length == 0) revert ZeroStringLength();
        if (bytes(addr).length == 0) revert ZeroStringLength();
        remoteAddressHashes[chain] = keccak256(bytes(_lowerCase(addr)));
        remoteAddresses[chain] = addr;
        emit TrustedAddressAdded(chain, addr);
    }

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

On token bridging, it will send message to AxelarGateway with lower case destination address due to string memory destinationAddress = remoteAddressValidator.getRemoteAddress(destinationChain); and remoteAddressValidator.getRemoteAddress returns lowercase address

    function _callContract(
        string calldata destinationChain,
        bytes memory payload,
        uint256 gasValue,
        address refundTo
    ) internal {
        string memory destinationAddress = remoteAddressValidator.getRemoteAddress(destinationChain);
        if (gasValue > 0) {
            gasService.payNativeGasForContractCall{ value: gasValue }(
                address(this),
                destinationChain,
                destinationAddress,
                payload,
                refundTo
            );
        }
        gateway.callContract(destinationChain, destinationAddress, payload);
    }

It will cause fund loss on bridging to that chain since it will always send messages to the contract address in lowercase format. But since that chain is case sensitive, the contract address in lowercase format is invalid and not found, so the token can't be unlocked on the destination chain and lost forever!

Tools Used

Manual review

Add a map to map if that chain wallet address is case sensitive and don't use _lowerCase on that chain.

Assessed type

Invalid Validation

#0 - c4-pre-sort

2023-07-29T00:13:09Z

0xSorryNotSorry marked the issue as duplicate of #323

#1 - c4-judge

2023-09-01T12:05:20Z

berndartmueller marked the issue as satisfactory

Findings Information

🌟 Selected for report: T1MOH

Also found by: Chom, UniversalCrypto

Labels

bug
2 (Med Risk)
satisfactory
edited-by-warden
duplicate-25

Awards

978.4802 USDC - $978.48

External Links

Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L502-L523 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L599-L615 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L41-L67

Vulnerability details

Impact

Non-EVM chain is supported on InterchainTokenService but doesn't support on InterchainProposalExecutor

The word "Interchain" means it should support Cosmos because it's a Cosmos terms. Google "Interchain" for proof.

Axelar isn't supporting only EVM but also non-EVM especially Cosmos which is their focus. So, I think this should be in scope.

If InterchainProposalExecutor doesn't support Cosmos, Cosmos governance modules such as the Axelar chain itself can't execute proposals on EVM chains.

Proof of Concept

InterchainTokenService support non-EVM wallet addresses for both send and receive as it uses bytes as destinationAddress.

    function transmitSendToken(
        bytes32 tokenId,
        address sourceAddress,
        string calldata destinationChain,
        bytes memory destinationAddress,
        uint256 amount,
        bytes calldata metadata
    ) external payable onlyTokenManager(tokenId) notPaused {
        bytes memory payload;
        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);
        payload = abi.encode(SELECTOR_SEND_TOKEN_WITH_DATA, tokenId, destinationAddress, amount, sourceAddress.toBytes(), metadata);
        _callContract(destinationChain, payload, msg.value, sourceAddress);
        emit TokenSentWithData(tokenId, destinationChain, destinationAddress, amount, sourceAddress, metadata);
    }
    function _processSendTokenPayload(string calldata sourceChain, bytes calldata payload) internal {
        (, bytes32 tokenId, bytes memory destinationAddressBytes, uint256 amount) = abi.decode(payload, (uint256, bytes32, bytes, uint256));
        bytes32 commandId;


        assembly {
            commandId := calldataload(4)
        }
        address destinationAddress = destinationAddressBytes.toAddress();
        ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
        address expressCaller = _popExpressReceiveToken(tokenId, destinationAddress, amount, commandId);
        if (expressCaller == address(0)) {
            amount = tokenManager.giveToken(destinationAddress, amount);
            emit TokenReceived(tokenId, sourceChain, destinationAddress, amount);
        } else {
            amount = tokenManager.giveToken(expressCaller, amount);
        }
    }

But on InterchainProposalExecutor, _execute which is a handler function that get executed on receiving message from Axelar use StringToAddress.toAddress(sourceAddress) that doesn't work in the case of non-EVM chains such as Cosmos. So, it can't execute any proposal proposed on a non-EVM chain. The Axelar chain itself is an example.

    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal override {
        _beforeProposalExecuted(sourceChain, sourceAddress, payload);

        // Check that the source address is whitelisted
        if (!whitelistedSenders[sourceChain][StringToAddress.toAddress(sourceAddress)]) {
            revert NotWhitelistedSourceAddress();
        }

        // Decode the payload
        (address interchainProposalCaller, InterchainCalls.Call[] memory calls) = abi.decode(payload, (address, InterchainCalls.Call[]));

        // Check that the caller is whitelisted
        if (!whitelistedCallers[sourceChain][interchainProposalCaller]) {
            revert NotWhitelistedCaller();
        }

        // Execute the proposal with the given arguments
        _executeProposal(calls);

        _onProposalExecuted(sourceChain, sourceAddress, interchainProposalCaller, payload);

        emit ProposalExecuted(keccak256(abi.encode(sourceChain, sourceAddress, interchainProposalCaller, payload)));
    }

Tools Used

Manual Review

Use keccak256(abi.encode(sourceAddress)) instead of StringToAddress.toAddress(sourceAddress)

Assessed type

Access Control

#0 - c4-pre-sort

2023-07-29T00:13:06Z

0xSorryNotSorry marked the issue as duplicate of #323

#1 - c4-judge

2023-09-01T11:59:51Z

berndartmueller marked the issue as not a duplicate

#2 - c4-judge

2023-09-01T12:06:42Z

berndartmueller marked the issue as duplicate of #149

#3 - c4-judge

2023-09-05T09:21:08Z

berndartmueller marked the issue as not a duplicate

#4 - c4-judge

2023-09-05T09:21:50Z

berndartmueller marked the issue as duplicate of #25

#5 - c4-judge

2023-09-05T09:22:03Z

berndartmueller marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter