code4rena-2024-08-chakra-h10

[H-10] ChakraSettlement.receive_cross_chain_msg and ChakraSettlement.receive_cross_chain_callback functions do not ensure that receiving ChakraSettlement contract’s contract_chain_name must match to_chain corresponding to respective txid input though they should

보고서

Summary

브릿지를 받을 때 toChain 파라미터가 자신의 체인이 맞는 지를 확인하지 않는다. 즉, 다른 체인이 목적지인 메시지를 등록해도 처리할 수 있으며, 다른 체인에서 사용된 브릿지 요청 서명을 재전송하면 토큰을 탈취할 수 있다.

Keyword

theft, lending protocol, replay attack, bridge, signature

Vulnerability

txid를 만들기 위해 해시할 때 toChain 를 포함하는 것으로 txid 에 목적지 체인에 대한 정보를 포함한다. 하지만 이 정책은 목적지 체인에서 자신의 체인 이름을 파라미터로 사용하며 txid를 직접 계산하여 사용할 때 유효한 것이다. receive_cross_chain_msg 함수는 txid 를 재계산하지 않고 입력받은 값을 그대로 이용한다. 즉, 현재 txid 는 검증 없이 사용되므로 toChain 역시 검증되지 않고 있다는 의미이며, 다른 체인이 목적지인 메시지를 등록해도 처리할 수 있다는 의미이다.

    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 {
        {
            // verify signature
            bytes32 message_hash = keccak256(
                abi.encodePacked(
@>                  txid,
                    from_chain,
                    from_address,
                    from_handler,
                    to_handler,
                    keccak256(payload)
                )
            );
 
            require(
@>              signature_verifier.verify(message_hash, signatures, sign_type),
                "Invalid signature"
            );
 
            require(
                receive_cross_txs[txid].status == CrossChainMsgStatus.Unknow,
                "Invalid transaction status"
            );
        }
 
        ...
    }

서명 replay attack을 통해 receive_cross_chain_msg 를 호출하면 토큰을 부정 수급할 수 있다. 서명 replay attack이 가능하려면 한 가지 조건이 있다. to_handler 와 동일한 주소에 handler 컨트랙트가 배포되어 있어야 한다. 동일한 트랜잭션 nonce 를 이용하여 배포하거나 create2를 통해 배포하는 경우 동일한 주소에 컨트랙트를 배포할 수 있다.

많은 프로토콜들이 멀티체인 지원을 할 때는 각 체인의 컨트랙트 주소를 동일하게 맞추곤 한다. 그리고 유저가 자신의 handler를 배포한 뒤 시스템에 연동할 수 있으므로 여러 체인에 동일 주소로 핸들러를 배포하는 것을 막을 수 없다. 따라서 동일 주소에 Handler 가 있을 수 있다는 가정은 현실적이다. 따라서 Settlement는 서명 해시에 to_chain 정보를 추가하여 서명 replay attack을 막을 책임이 있다.

Impact

서명을 재사용하여 토큰을 탈취할 수 있음

Mitigation

message_hash 에 목적지 체인 이름을 포함한다. ERC712 를 적용하는 것도 좋은 방법이다.

    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 {
        {
            // verify signature
            bytes32 message_hash = keccak256(
                abi.encodePacked(
                    txid,
                    from_chain,
                    from_address,
                    from_handler,
                    to_handler,
+                   keccak256(contract_chain_name), // toChain name
                    keccak256(payload)
                )
            );
 
            require(
                signature_verifier.verify(message_hash, signatures, sign_type),
                "Invalid signature"
            );
 
            require(
                receive_cross_txs[txid].status == CrossChainMsgStatus.Unknow,
                "Invalid transaction status"
            );
        }
 
        ...
    }

tags: bughunting, chakra, smart contract, starknet, cairo, bridge, cross chain, lack-of-input-validation-vul, signature, replay attack, crypto theft, severity high