Cross-chain reentrancy involves exploiting asynchronous transactions or inconsistencies between two blockchains that communicate with each other.
In this type of vulnerability, an attacker leverages the delay or difference in state updates across chains to manipulate the state on one chain based on outdated or unverified information from another chain.
Game
Consider how an attacker might exploit this contract if they can control or manipulate the calls to completeTransfer through cross-chain messaging.
How could an attacker use reentrant calls from one chain to the other to alter the balances in unintended ways?
// SPDX-License-Identifier: MIT// Open me in VSCode and really think before opening the hints!// Add @audit tags wherever suspicious// Go to the solidity docs to complete missing knowledge of what's happening here// Solve by drafting a fix!pragmasolidity ^0.8.0;contract CrossChainBridge {mapping(address=>uint256) public balances;eventTransferInitiated(addressindexed user, uint256 amount, string targetChain);eventTransferCompleted(addressindexed user, uint256 amount);// Function to start transferring funds to another chainfunctioninitiateTransfer(uint256 amount,stringmemory targetChain) public {require(balances[msg.sender] >= amount,"Insufficient balance"); balances[msg.sender] -= amount;emitTransferInitiated(msg.sender, amount, targetChain);// Assume the amount is now locked, awaiting confirmation from the target chain }// Function to receive funds back from the other chainfunctioncompleteTransfer(address user,uint256 amount) public { balances[user] += amount;emitTransferCompleted(user, amount); }}
Cross-chain reentrancy often involves delayed state changes and trust assumptions about calls between chains.
Look closely at completeTransfer and consider what happens if the function is called repeatedly or unexpectedly, especially if thereโs no verification of the source
Think about how an attacker could use completeTransfer to manipulate balances without sending actual funds from the other chain.
Since balances[user] is updated directly in completeTransfer, consider what could happen if they initiate a transfer and then repeatedly trigger completeTransfer from the "other chain."
functioncompleteTransfer(address user,uint256 amount) public {require(isTrustedSource(msg.sender),"Untrusted source"); // Fix: Example verificationrequire(!isProcessedTransaction(user, amount),"Already processed"); // Fix 2: Track processed transfers balances[user] += amount;markTransactionProcessed(user, amount); // Mark transaction as processedemitTransferCompleted(user, amount);}