code4rena-2024-08-axelar-m02
[M-02] TokenBalance limit could be bypassed by deploying TokenManager
Summary
Axelar 에 연동된 한 체인이 해킹당하더라도 ITSHub에서 잔고 추적을 통해 브릿지되었던 토큰보다 많이 이동할 수 없도록 방어하는데, 이를 우회하여 토큰을 탈취할 수 있다.
Keyword
theft, bridge, access control
Vulnerability
브릿지된 목적지 체인이 해킹당하더라도 ITSHub에서 각 체인 별 잔액을 추적하여 기존에 브릿지했던 것보다 많은 토큰을 꺼내갈 수 없도록 방어한다. 따라서 특정 체인이 해킹당하더라도 다른 체인의 자금은 영향이 없도록 한다. 그런데 이 잔액 추적 로직을 우회할 수 있는 가능성이 있다.
이 취약점은 Axelar에 연동된 체인 중 하나가 해킹되었다고 가정한다. 하나의 체인이 해킹되었을 때, 다른 체인을 원본 체인으로 둔 토큰을 탈취할 수 있음을 보인다. 구체적으로 어떻게 해킹되었는지는 논의하지 않는데, 체인 자체나 거버넌스가 해킹되었다고 가정하고 논지를 전개하는 것 같다.
ITSHub는 체인별 잔고를 추적한다. 하지만 아직 연동되지 않은 체인에서의 잔고는 추적하지 않는다. 또한, 연동되지 않는 체인으로 토큰을 전송하였을 때 ITSHub에서 이를 드랍하지 않고 무시한다.
fn apply_balance_tracking(
storage: &mut dyn Storage,
source_chain: ChainName,
destination_chain: ChainName,
message: &ItsMessage,
) -> Result<(), Error> {
match message {
ItsMessage::InterchainTransfer {
token_id, amount, ..
} => {
// Update the balance on the source chain
@> update_token_balance(
storage,
token_id.clone(),
source_chain.clone(),
*amount,
false,
)
.change_context_lazy(|| Error::BalanceUpdateFailed(source_chain, token_id.clone()))?;
// Update the balance on the destination chain
@> update_token_balance(
storage,
token_id.clone(),
destination_chain.clone(),
*amount,
true,
)
.change_context_lazy(|| {
Error::BalanceUpdateFailed(destination_chain, token_id.clone())
})?
}
...
};
Ok(())
}
pub fn update_token_balance(
storage: &mut dyn Storage,
token_id: TokenId,
chain: ChainName,
amount: Uint256,
is_deposit: bool,
) -> Result<(), Error> {
let key = TokenChainPair { token_id, chain };
let token_balance = TOKEN_BALANCES.may_load(storage, key.clone())?;
match token_balance {
Some(TokenBalance::Tracked(balance)) => {
let token_balance = if is_deposit {
balance
.checked_add(amount)
.map_err(|_| Error::MissingConfig)?
} else {
balance
.checked_sub(amount)
.map_err(|_| Error::MissingConfig)?
}
.then(TokenBalance::Tracked);
TOKEN_BALANCES.save(storage, key.clone(), &token_balance)?;
}
@> Some(_) | None => (), // 연동되지 않은(밸런스 초기화가 안된) 체인이면 잔고를 무시
}
Ok(())
}또한 일단 Axelar에 연동된 체인 사이라면, A 체인에서 B 체인에게 특정 토큰과 연동된 TokenManager를 배포시키는 일이 가능하다. 메시지를 받은 B 체인은 파라미터로 받은 주소의 토큰을 tokenId와 연결하며 이 토큰으로의 브릿지를 관리하는 토큰 매니저를 배포한다. (원래 tokenId 조작은 불가하지만 체인이 해킹됨을 가정하여 임의의 tokenId 로 배포 요청할 수 있다고 하는 것 같다..)
function deployTokenManager(
bytes32 salt,
string calldata destinationChain,
TokenManagerType tokenManagerType,
bytes calldata params,
uint256 gasValue
) external payable whenNotPaused returns (bytes32 tokenId) {
// Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType);
address deployer = msg.sender;
if (deployer == interchainTokenFactory) {
deployer = TOKEN_FACTORY_DEPLOYER;
}
tokenId = interchainTokenId(deployer, salt);
emit InterchainTokenIdClaimed(tokenId, deployer, salt);
if (bytes(destinationChain).length == 0) {
_deployTokenManager(tokenId, tokenManagerType, params);
} else {
@> _deployRemoteTokenManager(tokenId, destinationChain, gasValue, tokenManagerType, params);
}
}다음과 같은 상황을 생각해보자.
- 3개의 체인 이더리움, BNB, WoofChain 이 있다고 하자.
- TokenA는 이더리움, WoofChain 에 있는 ITS이다.
- TokenA의 원본 체인은 이더리움이므로 이더리움에서의 잔고는 ITSHub에서 추적되지 않는다.
- TokenA의 WoofChain 에서의 잔고는 1000이다.
- WoofChain은 해킹되었다. 원래라면 WoofChain애서 1000 초과의 TokenA를 다른 체인으로 브릿지할 수 없다. (공격을 통해 이 방어를 깰 것이다)
이 상태에서 다음과 같이 공격하여 잔고를 조작할 수 있다. 공격 시나리오는 다음과 같다.
- BNB에 FAKEToken을 배포한다. UINT256_MAX 만큼을 공격자에게 민팅한다.
- WoofChain → BNB 로 FAKEToken 을 관리하는 TokenManager 를 배포 요청한다.
- WoofChain이 해킹되었음을 가정하므로, 공격자가 자유롭게 잘못된 tokenId(TokenA와 동일한 tokenId)로 설정한 TokenManager를 배포 요청할 수 있다고 가정하는 것 같다…
- BNB 체인에는 FAKEToken이 연동된(브릿지시 소각 또는 락되는 토큰), 하지만 tokenId로 인해 TokenA로 취급되는 브릿지 경로가 만들어진다.
- BNB에서 10억개의 FAKEToken을 소각 또는 락하며 브릿지를 요청한다. ITSHub에서 WoofChain.TokenA의 잔고가 1,000,000,1000 로 증가한다.
- WoofChain에서 TokenA를 이더리움으로 브릿지하면 이더리움에 락되어 있던 토큰을 탈취할 수 있다.
발생할 수 있는 (연동된 체인에 발생할 수 있는)해킹 사고의 예로는 다음과 같다.
- Bad precompiles or revert handling: Godwoken, evmos
- Invalid transactions: frontier
- Consensus or malicious validator: NEAR
- Compiler bugs: vyper
- Event spoofing
- Contract compromise: e.g admin key, take over, bug
- amplifier/routing architecture compromise: External Gateway, Gateway for X on Axelar, - Prover, Verifier, Relayer
Impact
ITSHub에서 잔고 추적을 우회하여 토큰을 탈취할 수 있다.
Mitigation
ITSHub는 각 ITS 의 tokenId의 원본 체인을 저장하고 원본 체인에서만 토큰 또는 TokenManager 배포를 허용해야 한다. 이렇게 하면 원격 체인의 전송에 대한 액세스가 제한됩니다.
Memo
사실 이해가 잘 안 되는데, 연동된 체인을 구체적으로 어떻게 해킹하는지에 대해서는 생략하고, 그렇다고 가정한 뒤 논지를 전개하는 게 어색해서 같다. 구체적인 PoC를 제공하지는 않았지만 하나의 체인으로 인해 다른 체인에 영향을 끼칠 수 있다는 점, 그리고 ITSHub를 통해 방어하던 것을 깰 수 있다는 점으로 취약점으로 인정된 것 같다.
스폰서 역시 Axelar에 체인을 연동하기 위해서는 거버넌스의 허용이 필요하며, 그러기 위해서는 연동될 체인이 안전한지를 확인하기 때문에 실현 가능성은 낮다고 했다.
tags: bughunting, axelar, smart contract, solidity, rust, cosmwasm, bridge, crypto theft, access control vulnerability, solo issue, severity medium