code4rena-2024-08-chakra-h13
[H-13] The LockMint and BurnUnlock modes cannot be used
Summary
브릿지 핸들러가 LockMint 모드와 BurnUnlock 모드를 이용할 때, 메시지를 외부에서 수신한 경우 동작을 잘못 하였다. LockMint 모드일 때는 토큰을 언락해야 하고, BurnUnlock 모드일 때는 발행해야 하는데 그렇게 하지 않았다. 이로 인해 토큰이 잘못 브릿지된다.
Keyword
bridge, cross chain, business logic vul
Vulnerability
- solidity/handler/contracts/ChakraSettlementHandler.sol#L127
- solidity/handler/contracts/ChakraSettlementHandler.sol#L338
- cairo/handler/src/handler_erc20.cairo#L127
LockMint 모드와 BurnUnlock 모드는 다음과 같이 작동한다.
- LockMint: 출발지 체인에 토큰을 잠그고 목적지 체인에서 발행한다.
- BurnUnlock: 출발지 체인에서 소각하고 목적지 체인에서 토큰을 전송한다.
A → B 체인으로 LockMint 모드로 브릿지된 토큰을 다시 B → A 체인으로 브릿지해온다면 A 체인 핸들러에 예치된 토큰을 언락해야 한다. 하지만 핸들러는 이를 잘못 핸들링하고 있다.
cross_chain_erc20_settlement 는 외부로 메시지를 송신할 때 처리하는 함수이다. 핸들러가 LockMint 모드이므로 토큰을 컨트랙트에 잠근다.
receive_cross_chain_msg는 외부에서 메시지를 수신했을 때 처리하는 함수이다. 잠긴 언락하지 않고 발행하고 있다.
BurnUnlock 모드에서도 유사한 문제가 발생한다. B → A 체인으로 다시 브릿지해온다면 A 체인에서는 토큰을 발행해야 한다. 하지만 그 대신 언락을 한다.
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);
}
...
}
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) {
...
if (payload_type == PayloadType.ERC20) {
// Cross chain transfer
{
// Decode transfer payload
...
@> } else if (mode == SettlementMode.LockMint) {
@> _erc20_mint(
AddressCast.to_address(transfer_payload.to),
transfer_payload.amount
);
return true;
@> } else if (mode == SettlementMode.BurnUnlock) {
@> _erc20_unlock(
AddressCast.to_address(transfer_payload.to),
transfer_payload.amount
);
return true;
}
...
}
}
return false;
}Cairo 컨트랙트에도 동일한 문제가 있다.
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{
...
}else if self.mode.read() == SettlementMode::LockMint{
@> erc20.mint_to(u256_to_contract_address(transfer.to), transfer.amount);
} else if self.mode.read() == SettlementMode::BurnUnlock{
@> token.transfer(u256_to_contract_address(transfer.to), transfer.amount);
}
...
}Impact
LockMint 모드와 BurnUnlock 모드가 잘못 작동한다. 토큰이 잘못 브릿지된다.
Mitigation
LockMint 모드일 때 외부에서 메시지를 수신한다면 토큰을 언락한다. BurnUnlock 모드일 때 외부에서 메시지를 수신한다면 토큰을 발행한다.
tags: bughunting, chakra, smart contract, starknet, cairo, solidity, bridge, cross chain, severity high, business-logic-vul