Platform: Code4rena
Start Date: 01/12/2022
Pot Size: $26,900 USDC
Total HM: 3
Participants: 19
Period: 4 days
Judge: GalloDaSballo
Id: 188
League: ETH
Rank: 2/19
Findings: 1
Award: $3,233.62
π Selected for report: 0
π Solo Findings: 0
3233.6158 USDC - $3,233.62
Arbitrum relaying and transaction execution can be abused
uint256 _ticketID = inbox.createRetryableTicket{ value: msg.value }( address(executor), 0, _maxSubmissionCost, msg.sender, msg.sender, _gasLimit, _gasPriceBid, _data );
When EthereumToArbitrumRelayer#processCalls
is called it creates a retryable ticket with msg.sender
as the beneficiary
of the L2 message. Arbitrum allows the beneficiary to cancel a retryable ticket at anytime. In EthereumToArbitrumExecutor
there is no way to cancel a relayed call, only offering replay protection. Additionally EthereumToArbitrumRelayer#processCalls
doesn't offer any replay protection, allowing the same transaction to be queue up by whoever, whenever and as many times as desired. If the transaction is executed then there is nothing to worry about because of the replay protection provided by EthereumToArbitrumExecutor
but can create issues when the call doesn't succeed.
EthereumToArbitrumRelayer
use msg.sender
as beneficiary so whoever processes the calls will be the only party able to cancel the message. This is problematic because EthereumToArbitrumRelayer#processCalls
doesn't implement any replay protection.
Example:
Assume you have two users, User A
and User B
. User A
relays a transaction then processes the call. They realize they have made a mistake to so they cancel their message to prevent execution, which they are allowed to do since they are beneficiary
. Now at any point User B
can process User A's
calls. This time around User B
is beneficiary
and User A
can't cancel the message, allowing it to be executed.
The consequences of this can be extremely damaging. For example, if User A
cancels the message then relays another similar transaction it may result in an effective replay attack because they wrongly assume their transaction is permanently canceled. Another example would be a set of calls that fails when executed. Imagine User A
relays a call that fails when executed today. Months later conditions have changed and execution of that call will now succeed. Their call from months ago can now be re-processed and executed.
Manual Review
Create a method in CrossChainExecutorArbitrum
that allows a sender to cancel their call. I also recommend requiring a expiration timestamp by which a relayed call must be processed. I do not recommend implementing replay protection on CrossChainRelayerArbitrum
. It can lead to DOS'ing, since msg.sender is the beneficiary. Any user would be able to queue a transaction then immediately cancel the message to DOS the request.
#0 - GalloDaSballo
2022-12-11T20:28:23Z
Keeping unique for now due to the Arbitrum Twist, unclear on severity
#1 - c4-judge
2022-12-11T20:28:29Z
GalloDaSballo marked the issue as primary issue
#2 - c4-sponsor
2022-12-14T21:47:14Z
PierrickGT marked the issue as sponsor confirmed
#3 - c4-sponsor
2022-12-14T21:47:17Z
PierrickGT marked the issue as disagree with severity
#4 - PierrickGT
2022-12-14T21:47:36Z
Duplicate of https://github.com/code-423n4/2022-12-pooltogether-findings/issues/60 Should be downgraded to a 2 (Med Risk) issue.
#5 - GalloDaSballo
2022-12-26T20:43:15Z
Marking as dup as less straightforward vs 60
#6 - c4-judge
2022-12-26T20:43:30Z
GalloDaSballo marked the issue as duplicate of #60
#7 - c4-judge
2022-12-26T20:43:35Z
GalloDaSballo changed the severity to 2 (Med Risk)
#8 - c4-judge
2022-12-26T23:43:13Z
GalloDaSballo marked the issue as satisfactory