code4rena-2024-08-chakra-m10

[M-10] Does not check if to_chain and to_handler is whitelisted in cross_chain_erc20_settlement

보고서

Summary

출발지에서 메시지를 보낼 때, 화이트리스트에 등록되어 있는 목적지로 보내는 지를 확인하지 않는다. 이로 인해 유저는 잘못 요청할 수 있고, 이렇게 잘못 요청된 자산은 잠긴다.

Keyword

bridge, cross chain, lack of input validation vul, asset lock

Vulnerability

Handler.cross_chain_erc20_settlement 에서 to_chainto_handler 가 화이트리스트에 등록되어 있는 지 확인하지 않는다. 따라서 이 핸들러와 매치되지 않는 임의의 체인, 핸들러로 메시지를 보낼 수 있다.

function cross_chain_erc20_settlement(
    string memory to_chain,
    uint256 to_handler,
    uint256 to_token,
    uint256 to,
    uint256 amount
) external {
    require(amount > 0, "Amount must be greater than 0");
    require(to != 0, "Invalid to address");
@>  require(to_handler != 0, "Invalid to handler address");
    require(to_token != 0, "Invalid to token address");
 
    if (mode == SettlementMode.MintBurn) {
        _erc20_lock(msg.sender, address(this), amount);
    } else if (mode == SettlementMode.LockUnlock) {
        _erc20_lock(msg.sender, address(this), amount);
    } else if (mode == SettlementMode.LockMint) {
        _erc20_lock(msg.sender, address(this), amount);
    } else if (mode == SettlementMode.BurnUnlock) {
        _erc20_burn(msg.sender, amount);
    }
    ...
}

만약 to_handler 로 컨트랙트가 아닌 주소를 설정했다면 목적지 체인에서 Handler.receive_cross_chain_msg 를 호출할 수 없어 트랜잭션이 revert 된다. 따라서 실패를 알리기 위한 이벤트를 발생시킬 수 없어 실패에 대한 핸들링도 불가하다.

// Settlement.receive_cross_chain_msg
function receive_cross_chain_msg(
    uint256 txid,
    string memory from_chain,
    uint256 from_address,
    uint256 from_handler,
    address to_handler,
    PayloadType payload_type,
    bytes calldata payload,
    uint8 sign_type, // validators signature type /  multisig or bls sr25519
    bytes calldata signatures // signature array
) external {
    ...
 
@>  bool result = ISettlementHandler(to_handler).receive_cross_chain_msg( // this will be reverted
        txid,
        from_chain,
        from_address,
        from_handler,
        payload_type,
        payload,
        sign_type,
        signatures
    );
  
    CrossChainMsgStatus status = CrossChainMsgStatus.Failed;
    if (result == true) {
        status = CrossChainMsgStatus.Success;
        receive_cross_txs[txid].status = CrossChainMsgStatus.Success;
    } else {
@>      receive_cross_txs[txid].status = CrossChainMsgStatus.Failed;
    }
 
    emit CrossChainHandleResult(
        txid,
@>      status,
        contract_chain_name,
        from_chain,
        address(to_handler),
        from_handler,
        payload_type
    );
}

또한 한가지 문제가 더 있다. to_handler 가 핸들러 컨트랙트의 주소이긴 하지만, from_handler와 매치되지 않는 핸들러일 수 있다. 화이트리스트에 등록되지 않은 핸들러가 메시지를 요청했다면 Handler.receive_cross_chain_msgfalse 를 리턴하여 실패를 알린다. Validator 는 CrossChainHandleResult 이벤트를 리스닝하여 receive_cross_chain_callback 을 통해 source 체인에게 실패를 알리고 핸들러가 실패를 처리하게 한다.

function receive_cross_chain_msg(
    uint256 /**txid */,
    string memory from_chain,
    uint256 /**from_address */,
    uint256 from_handler,
    PayloadType payload_type,
    bytes calldata payload,
    uint8 /**sign type */,
    bytes calldata /**signaturs */
) external onlySettlement returns (bool) {
    //  from_handler need in whitelist
@>  if (is_valid_handler(from_chain, from_handler) == false) {
@>      return false;
    }
    ...
}

하지만 Handler.receive_cross_chain_callback 에서는 오로지 화이트리스트에 등록된 체인과 핸들러만 콜백 메시지를 보낼 수 있도록 제한한다. 즉, 잘못된 체인-핸들러로 메시지를 보냈을 때 실패했음을 callback 으로 알리지만, 정작 출발지 체인의 핸들러는 잘못된 체인-핸들러로 인한 실패 상황을 핸들링하지 못하고 무시한다.

function receive_cross_chain_callback(
    uint256 txid,
    string memory from_chain,
    uint256 from_handler,
    CrossChainMsgStatus status,
    uint8 /* sign_type */, // validators signature type /  multisig or bls sr25519
    bytes calldata /* signatures */
) external onlySettlement returns (bool) {
    //  from_handler need in whitelist
@>  if (is_valid_handler(from_chain, from_handler) == false) {
@>      return false;
    }
 
    require(
        create_cross_txs[txid].status == CrossChainTxStatus.Pending,
        "invalid CrossChainTxStatus"
    );
 
    if (status == CrossChainMsgStatus.Success) {
        if (mode == SettlementMode.MintBurn) {
            _erc20_burn(address(this), create_cross_txs[txid].amount);
        }
 
        create_cross_txs[txid].status = CrossChainTxStatus.Settled;
    }
 
    if (status == CrossChainMsgStatus.Failed) {
        create_cross_txs[txid].status = CrossChainTxStatus.Failed;
    }
 
    return true;
}

Impact

핸들러가 잘못된 체인과 핸들러로 토큰을 보내는 것을 막지 않는다. 잘못된 핸들러에게 메시지를 보내면 receive_cross_chain_callback 를 통해 실패를 핸들링할 수도 없다.

Mitigation

잘못된 체인-핸들러로 메시지 전송을 요청할 수 없도록 Handler.cross_chain_erc20_settlement 에서 to_chainto_handler 가 올바른지 확인한다.


tags: bughunting, chakra, smart contract, starknet, solidity, bridge, cross chain, lack-of-input-validation-vul, asset lock, severity medium