code4rena-2024-08-chakra-h05

[H-05] settlement.cairo doesn’t process callback correctly leading to CrossChainMsgStatus marked as SUCCESS even if it failed on destination chain

보고서

Summary

목적지 체인에서의 성공 여부를 저장/사용해야 하는 곳에 잘못된 값을 기록하였다. 목적지 체인에서 실패했더라도 성공으로 표시될 수 있다. 브릿지이기 때문에 잘못된 상태나 이벤트를 발생하는 것이 크리티컬한 이슈로 취급되었다.

Keyword

bridge, cross chain, wrong state, event

Vulnerability

크로스체인으로 메시지를 보낸 후 목적지 체인에서 처리, 처리 결과를 다시 출발지 체인의 receive_cross_chain_callback 를 통해 전달한다. 파라미터 cross_chain_msg_status는 목적지 체인에서의 성공 여부를 나타낸다. 그런데 cross_chain_msg_status 값은 created_tx에 저장되지 않는다. created_tx에 저장되는 값은 handler.receive_cross_chain_callback의 리턴값이며, 이 함수가 실패하지 않는다면 함상 true를 리턴한다. 이 함수가 실패한다면 트랜잭션이 취소되므로 false를 리턴하지 않는다. 따라서 실제로 목적지에서 성공했는지 여부를 저장하지 않고, 모든 실행 결과는 SUCCESS 상태로 저장된다.

fn receive_cross_chain_callback(
    ref self: ContractState,
    cross_chain_msg_id: felt252,
    from_chain: felt252,
    to_chain: felt252,
    from_handler: u256,
    to_handler: ContractAddress,
@>  cross_chain_msg_status: u8,
    sign_type: u8,
    signatures: Array<(felt252, felt252, bool)>,
) -> bool {
    ...
 
    let mut message_hash_temp: felt252 = LegacyHash::hash(from_chain, (cross_chain_msg_id, to_chain, from_handler, to_handler));
    let message_hash_final:felt252 = LegacyHash::hash(message_hash_temp, cross_chain_msg_status);
    self.check_chakra_signatures(message_hash_final, signatures);
    let handler = IHandlerDispatcher{contract_address: to_handler};
@>  let success = handler.receive_cross_chain_callback(cross_chain_msg_id, from_chain, to_chain, from_handler, to_handler , cross_chain_msg_status);
    let mut state = CrossChainMsgStatus::PENDING;
@>  if success{ // handler.receive_cross_chain_callback 가 실패하지 않는다면 항상 true 리턴
@>      state = CrossChainMsgStatus::SUCCESS;
    }else{
@>      state = CrossChainMsgStatus::FAILED;
    }
 
    self.created_tx.write(cross_chain_msg_id, CreatedTx{
        tx_id:cross_chain_msg_id,
@>      tx_status:state,
        from_chain: to_chain,
        to_chain: from_chain,
        from_handler: to_handler,
        to_handler: from_handler
    });
 
    self.emit(CrossChainResult {
        cross_chain_settlement_id: cross_chain_msg_id,
        from_address: get_tx_info().unbox().account_contract_address,
        from_chain: to_chain,
        from_handler: to_handler,
        to_chain: from_chain,
        to_handler: from_handler,
@>      cross_chain_msg_status: state,
    });
    return true;
}

Impact

목적지 체인에서의 성공 여부를 저장/사용해야 하는 곳에 잘못된 값이 기록된다. 목적지 체인에서 실패했더라도 성공으로 표시될 수 있다.

Mitigation

handler.receive_cross_chain_callback 가 성공했을 시 목적지 체인에서의 상태를 기록한다.

    let success = handler.receive_cross_chain_callback(cross_chain_msg_id, from_chain, to_chain, from_handler, to_handler , cross_chain_msg_status);
    let mut state = CrossChainMsgStatus::PENDING;
    if success{
-       state = CrossChainMsgStatus::SUCCESS;
+       state = cross_chain_msg_status;
    }else{
        state = CrossChainMsgStatus::FAILED;
    }

tags: bughunting, chakra, smart contract, starknet, cairo, bridge, cross chain, severity high, wrong state, cairo event