If the seller is blacklisted during the escrow process, fund recovery from the contract relies solely on the arbiter's intervention. Nonetheless, an attacker can obstruct the arbiter from executing resolveDispute()
, resulting in funds being locked in the escrow contract indefinitely.
There are only two possible ways to transfer funds out of the Escrow contract, either by calling confirmReceipt()
or resolveDispute()
. On the situation of the seller becoming blacklisted (if the token supports this, e.g USDC, USDT, etc) it is impossible to call confirmReceipt()
as every call will revert due to the blacklist of the seller's address. Therefore the only way for recovering the funds in the Escrow contract is through the resolveDispute()
function. Unfortunately, under these circumstances resolveDispute()
is susceptible to being blocked by an attacker, as will be described below.
Consider an example Escrow contract with 10000 USDC, in the above case (seller address gets blacklisted) the funds could be recovered only if the arbiter calls resolveDispute()
with buyerAward = price - arbiterfee
, this way during the execution of resolveDispute()
, after the funds are transfered first to the buyer and the arbiter and the remaining balance on the contract will be zero, therefore skipping the transfer to the seller and thus sucessfully getting the funds out of the Escrow contract.
However, if a contract is in this situation, a malicious actor can exploit this to block the recover of funds by frontrunning the arbiters' resolveDispute()
with a minimum deposit of the selected token to the contract. As a consequence, after the transfers to the buyer and the arbiter the balance will not be zero, thus when trying to transfer the remaining funds to the buyer, it will revert due to the blacklist. See the example scenario below. There is a deployed escrow with 10000 USDC (USDC balance = 1e10) balance and with arbiterfee
set to 500 USDC. for whatever reason the seller gets blacklisted, making impossible to call confirmReceipt()
.
Arbiter has to interfere as it is the only way to recover the funds from the escrow. Therefore he calculate buyerAward
to price - arbiterFee = 10000000000 - 500000000 = 9500000000
and calls resolveDispute(9500000000)
.
The attacker is monitoring the mempool, see the arbiter transaction and tries to frontrun it by transfering 1 unit of USDC to the escrow addresss.
If the attacker succeed in frontrunning the arbiter, the Escrow balance is now higher than the arbiter calculations, therefore when the arbiter's resolveDispute()
call is being executed it will revert, as shown in the resolveDispute()
below.
In scenarios where the seller is blacklisted during the escrow process, an attacker can block the arbiter's attempts to recover funds back. He can repeat the attack every time the arbiter tries to call resolveDispute()
, locking the funds in the escrow contract for an indeterminate amount of time.
Manual Review
Consider changing resolveDispute()
so that the arbiter specifies both the sellerAward
and also the buyerAward
. This way the arbiter can guarantee that no matter which party (seller or buyer) gets blacklisted, the funds can be recovered, because he controls exactly how much tokens will be transferred and therefore not susceptible to the attack described above.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.