Platform: Code4rena
Start Date: 01/09/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 70
Period: 6 days
Judge: kirk-baird
Id: 281
League: ETH
Rank: 6/70
Findings: 2
Award: $1,598.79
π Selected for report: 0
π Solo Findings: 0
1333.1052 USDC - $1,333.11
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L349 https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L79
It is very common that user use multi-signature smart contract wallet to manage their digital assets for safety. Gnosis Safe is a multi-signature wallet and it is widely used.
Unlike EOA wallet, user owns a smart-contract wallet in one chain doesn't mean they own same smart-contract wallet in other chains.
When generating payload in SourceBridge.sol, msg.sender is assumed to be the receiver in destination chain to receive minted token, but the address in destination chain may not exist or controlled by malicious one if smart contract wallet is used, and all minted token could be locked forever or stolen by malicious attacker.
Check https://mirror.xyz/0xbuidlerdao.eth/lOE5VN-BHI0olGOXe27F0auviIuoSlnou_9t3XRJseY to find out how 20 million $OP got stolen.
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/SourceBridge.sol#L79
msg.sender
is encoded in payload
and sent to destination chain.
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L90-L91
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L109
msg.sender
is decoded from payload
and stored as txnHashToTransaction[txnHash].sender
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L349
token is minted to txnHashToTransaction[txnHash].sender
aka msg.sender
.
If msg.sender
is a smart-contract address, There is a high possibility that all bridged tokens are locked forever or stolen by malicious user because those who bridge tokens may not have privilege to access it.
Manual Review
Add parameter address receiver
in SourceBridge#burnAndCallAxelar()
to let user choose which address in destination chain they want to bridge. receiver
should be encoded in payload
and decoded from payload
in DestinationBridge#_execute()
Other
#0 - c4-pre-sort
2023-09-07T21:56:58Z
raymondfam marked the issue as primary issue
#1 - c4-pre-sort
2023-09-07T21:57:08Z
raymondfam marked the issue as sufficient quality report
#2 - c4-pre-sort
2023-09-10T07:43:23Z
raymondfam marked the issue as high quality report
#3 - c4-sponsor
2023-09-13T15:09:08Z
tom2o17 marked the issue as disagree with severity
#4 - c4-sponsor
2023-09-13T15:09:14Z
tom2o17 (sponsor) acknowledged
#5 - tom2o17
2023-09-13T15:09:55Z
Will double check in Front End that wallet has 0 codesize, This is not part of the initial bridging specs. -> EOA wallets only
#6 - c4-judge
2023-09-17T06:07:36Z
kirk-baird marked the issue as duplicate of #406
#7 - c4-judge
2023-09-17T06:10:30Z
kirk-baird changed the severity to 2 (Med Risk)
#8 - c4-judge
2023-09-17T06:10:52Z
kirk-baird marked the issue as satisfactory
π Selected for report: Udsen
Also found by: 0xDING99YA, 0xpiken, Inspecktor, SpicyMeatball, adriro, ast3ros, bin2chen, bowtiedvirus, kutugu, pep7siup, seerether
265.675 USDC - $265.68
When user bridges same amount
of tokens from different chains to the same destination chain, there is a possibility that txnHash
could be duplicated. In this case the previous transaction and thresholds may be rewrote and only the last one can be executed successfully, which means user may only receive one amount
of tokens in destination chain and the rest will be lost forever.
Alice owns some TOKEN respectively in the same EOA address on A chain and B chain.
1.Alice bridge 1 TOKEN from A chain to C chain.(Transaction 1)
2.Alice bridge 1 TOKEN from B chain to C chain at the same time. (Transaction 2)
Suppose any transaction no more than 10 TOKEN needs 3 approvals. it could happen as below:
Transaction 1 is stored after DestinationBridge#_execute() is executed successfully.
Bridged token in Transaction 1 is not minted to receiver because it needs two more approvals.
At this time transaction 2 is triggered, by chance it has the same nonce in payload
with transaction 1, transaction will be erased and replaced to transaction 2 since calculated txnHashs are same in two transactions because of the same payload
is used for calculation.
Only 1 TOKEN in transaction 2 will be minted to receiver. the other 1 TOKEN will be lost forever.
Manual Review
Calculate txnHash
using the combination of srcChain
, srcAddress
and payload
:
bytes32 txnHash = keccak256(abi.encode(srcChain, srcAddress, payload));
Other
#0 - raymondfam
2023-09-07T22:07:43Z
Very edge case.
#1 - c4-pre-sort
2023-09-07T22:07:50Z
raymondfam marked the issue as low quality report
#2 - c4-pre-sort
2023-09-07T22:07:56Z
raymondfam marked the issue as primary issue
#3 - c4-pre-sort
2023-09-07T23:05:49Z
raymondfam marked the issue as duplicate of #162
#4 - c4-pre-sort
2023-09-07T23:06:03Z
raymondfam marked the issue as remove high or low quality report
#5 - c4-pre-sort
2023-09-07T23:06:08Z
raymondfam marked the issue as sufficient quality report
#6 - c4-judge
2023-09-17T06:47:04Z
kirk-baird marked the issue as satisfactory
#7 - c4-judge
2023-09-26T03:03:40Z
kirk-baird changed the severity to 2 (Med Risk)
π Selected for report: Udsen
Also found by: 0xDING99YA, 0xpiken, Inspecktor, SpicyMeatball, adriro, ast3ros, bin2chen, bowtiedvirus, kutugu, pep7siup, seerether
265.675 USDC - $265.68
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L102
If the addresses of SourceBridge smart contracts in different chains are same, conflicts will occur and Most of bridging transactions from these chains will be reverted.
isSpentNonce[chainToApprovedSender[srcChain]][nonce]
, aka isSpentNonce[keccak256(abi.encode(0x1A...6C))][0]
was set to true
isSpentNonce[keccak256(abi.encode(0x1A...6C))][0]
has been set to true
, this bridging transaction will be reverted because the address of SourceBridge in B chain is same as the address of SourceBridge in A chain.Manual Review
Change the code as below:
function _execute( string calldata srcChain, string calldata srcAddr, bytes calldata payload ) internal override whenNotPaused { (bytes32 version, address srcSender, uint256 amt, uint256 nonce) = abi .decode(payload, (bytes32, address, uint256, uint256)); if (version != VERSION) { revert InvalidVersion(); } if (chainToApprovedSender[srcChain] == bytes32(0)) { revert ChainNotSupported(); } if (chainToApprovedSender[srcChain] != keccak256(abi.encode(srcAddr))) { revert SourceNotSupported(); } //@audit-info change the line below if (isSpentNonce[keccak256(abi.encode(srcChain, chainToApprovedSender[srcChain]))][nonce]) { revert NonceSpent(); } //@audit-info change the line below isSpentNonce[keccak256(abi.encode(srcChain, chainToApprovedSender[srcChain]))][nonce] = true; bytes32 txnHash = keccak256(payload); txnHashToTransaction[txnHash] = Transaction(srcSender, amt); _attachThreshold(amt, txnHash, srcChain); _approve(txnHash); _mintIfThresholdMet(txnHash); emit MessageReceived(srcChain, srcSender, amt, nonce); }
Other
#0 - raymondfam
2023-09-08T05:27:05Z
Nonces will have to be the same.
#1 - c4-pre-sort
2023-09-08T05:27:12Z
raymondfam marked the issue as low quality report
#2 - c4-pre-sort
2023-09-08T05:27:17Z
raymondfam marked the issue as primary issue
#3 - c4-pre-sort
2023-09-09T23:17:46Z
raymondfam marked the issue as duplicate of #162
#4 - c4-pre-sort
2023-09-09T23:17:52Z
raymondfam marked the issue as sufficient quality report
#5 - c4-judge
2023-09-17T06:39:05Z
kirk-baird changed the severity to 3 (High Risk)
#6 - c4-judge
2023-09-17T06:46:48Z
kirk-baird marked the issue as partial-50
#7 - c4-judge
2023-09-26T03:03:41Z
kirk-baird changed the severity to 2 (Med Risk)