code-423n4 / 2023-07-axelar-findings

2 stars 0 forks source link

The attacker can block canonical Tokens creation. #209

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L170-L173 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/utils/TokenManagerDeployer.sol#L33-L43

Vulnerability details

Impact

The attacker can block canonical Tokens creation.

Proof of Concept

To be able to deploy canonical tokens, we need to call registerCanonicalToken() and deployRemoteCanonicalToken() functions. These functions will deploy token managers and then deploy the token.

function registerCanonicalToken(address tokenAddress) external payable notPaused returns (bytes32 tokenId) {
        (, string memory tokenSymbol, ) = _validateToken(tokenAddress);
        if (gateway.tokenAddresses(tokenSymbol) == tokenAddress) revert GatewayToken();
        tokenId = getCanonicalTokenId(tokenAddress);
        _deployTokenManager(tokenId, TokenManagerType.LOCK_UNLOCK, abi.encode(address(this).toBytes(), tokenAddress)); //@audit here !
    }

registerCanonicalToken() function uses interchainTokenService address and tokenAdress as params. And these params will affect the creation of the token manager, as we see below. (bytecode and address)

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); //@audit
        address tokenManagerAddress = deployer.deploy(bytecode, tokenId); //@audit here !
        if (tokenManagerAddress.code.length == 0) revert TokenManagerDeploymentFailed();
    } 

The problem is , anyone can call deployTokenManager() directly with different bytes calldata params and deploy different token manager.(address and bytecode)

This should cause trouble because,

First, any other token manager cant be deployed because they are using the same salt for tokenId. Second, canonical token deployment is blocked because when the user wants to continue the process and call the deployRemoteCanonicalToken() function,

function deployRemoteCanonicalToken(
        bytes32 tokenId,
        string calldata destinationChain,
        uint256 gasValue
    ) public payable notPaused {
        address tokenAddress = getValidTokenManagerAddress(tokenId); //@audit here !
        tokenAddress = ITokenManager(tokenAddress).tokenAddress();
        if (getCanonicalTokenId(tokenAddress) != tokenId) revert NotCanonicalTokenManager();
        (string memory tokenName, string memory tokenSymbol, uint8 tokenDecimals) = _validateToken(tokenAddress);
        _deployRemoteStandardizedToken(tokenId, tokenName, tokenSymbol, tokenDecimals, '', '', destinationChain, gasValue);
    }

deployRemoteCanonicalToken() function will call getValidTokenManagerAddress() for tokenId , and that call will revert because the attacker changed deployment parameters.

function getTokenManagerAddress(bytes32 tokenId) public view returns (address tokenManagerAddress) {
        tokenManagerAddress = deployer.deployedAddress(address(this), tokenId); //@audit here !
    }
function getValidTokenManagerAddress(bytes32 tokenId) public view returns (address tokenManagerAddress) {
        tokenManagerAddress = getTokenManagerAddress(tokenId);
        if (ITokenManagerProxy(tokenManagerAddress).tokenId() != tokenId) revert TokenManagerDoesNotExist(tokenId); //@audit here !
    }

Tools Used

Manual review

Recommended Mitigation Steps

Not being able to call the deployTokenManager() function freely will solve the issue.

Assessed type

Other

0xSorryNotSorry commented 1 year ago

The final salt includes msg.sender in Create3 contract as below;

    function deploy(bytes calldata bytecode, bytes32 salt) external returns (address deployedAddress_) {
        bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt));
        deployedAddress_ = Create3.deploy(deploySalt, bytecode);

        emit Deployed(keccak256(bytecode), salt, deployedAddress_);
    }

Invalid assumption.

c4-pre-sort commented 1 year ago

0xSorryNotSorry marked the issue as low quality report

c4-judge commented 1 year ago

berndartmueller marked the issue as unsatisfactory: Invalid