code4rena-2024-08-chakra-h04

[H-04] In settlement.cairo::receive_cross_chain_msg - the message will always be marked with Status::SUCCESS

보고서

Summary

스타크넷쪽 컨트랙트는 EVM쪽 컨트랙트의 구현과는 다르게, 메시지를 수신했을 때 메시지에 오류가 있더라도 항상 성공했다고 기록한다. 잘못된 상태를 기록하고 이벤트를 발생한다.

Keyword

bridge, cross chain, wrong state, event

Vulnerability

settlement.cairo::receive_cross_chain_msg 함수를 통해 메시지를 수신할 때 handler.receive_cross_chain_msg 함수가 호출되며, 해당 호출의 반환 값을 사용하여 메시지에 SUCCESS 또는 FAILED 상태를 표시한다.

그런데 handler.receive_cross_chain_msg 함수는 실제 결과와 상관없이 항상 true를 반환한다. 따라서 모든 메시지를 성공으로 표시한다. 이로 인해 메시지 상태가 정확히 추적되어야 한다는 주요 불변 조건 중 하나가 위반된다.

 
// settlement.cairo::receive_cross_chain_msg
fn receive_cross_chain_msg(
    ref self: ContractState,
    cross_chain_msg_id: u256,
    from_chain: felt252,
    to_chain: felt252,
    from_handler: u256,
    to_handler: ContractAddress,
    sign_type: u8,
    signatures: Array<(felt252, felt252, bool)>,
    payload: Array<u8>,
    payload_type: u8,
) -> bool {
    ...
@>  let success = handler.receive_cross_chain_msg(cross_chain_msg_id, from_chain, to_chain, from_handler, to_handler , payload);
 
    let mut status = CrossChainMsgStatus::SUCCESS;
    if success{
@>      status = CrossChainMsgStatus::SUCCESS;
    }else{
@>      status = CrossChainMsgStatus::FAILED;
    }
 
    self.received_tx.write(cross_chain_msg_id, ReceivedTx{
        tx_id:cross_chain_msg_id,
        from_chain: from_chain,
        from_handler: from_handler,
        to_chain: to_chain,
        to_handler: to_handler,
@>      tx_status: status
    });
 
    // emit event
    self.emit(CrossChainHandleResult{
        cross_chain_settlement_id: cross_chain_msg_id,
        from_chain: to_chain,
        from_handler: to_handler,
        to_chain: from_chain,
        to_handler: from_handler,
@>      cross_chain_msg_status: status,
        payload_type: payload_type
    });
    return true;
}
 
// handler.receive_cross_chain_msg
fn receive_cross_chain_msg(ref self: ContractState, cross_chain_msg_id: u256, from_chain: felt252, to_chain: felt252,
from_handler: u256, to_handler: ContractAddress, payload: Array<u8>) -> bool{
    assert(to_handler == get_contract_address(),'error to_handler');
 
    assert(self.settlement_address.read() == get_caller_address(), 'not settlement');
 
    assert(self.support_handler.read((from_chain, from_handler)) && 
            self.support_handler.read((to_chain, contract_address_to_u256(to_handler))), 'not support handler');
 
    ...
    
@>  return true;
}

Impact

메시지 상태가 정확히 추적되어야 한다는 주요 불변 조건 중 하나가 위반된다. 잘못된 상태를 저장하고 이벤트를 발생한다. Solidity 버전과의 작동이 일치하지 않는다.

Mitigation

잘못된 상황에는 false를 리턴하도록 한다.

Memo

QA로 넘어갈 수도 있었는데 High로 올라갔다. 이상 상황에 트랜잭션을 취소시키므로 딱히 문제가 없어보이기도 한다. 하지만 Solidity 버전과의 작동 차이가 있다는 점으로(Solidity에서는 트랜잭션을 취소시키지 않고 false를 리턴하여 상태를 저장한다) 인해 인정된 것 같다. 아무래도 브릿지라서 그런 것 같다.


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