A malicious seller contract can disrupt the resolveDispute function.
When the buyer is creating a new escrow contract with the newEscrow function using an ERC777 or any other call on transfer token as the tokenContract, seller can provide a contract address to the buyer for the seller parameter. A step by step example attack would be:
Buyer created the new escrow contract with the token contract being an ERC777 or any other call on transfer token.
Seller refuses to do the work buyer asked for with the agreed price.
Buyer calls the initiateDispute
function to get their funds back.
Arbiter calls the resolveDispute
function with the buyerAward parameter being balanceOf(contract) - arbiterFee
expecting the funds minus the arbiterFee going back to the buyer.
Seller’s bot seeing this transcation call makes a front running attack to send 1 token to the contract.
resolveDispute
function reaches line 125 where tokenBalance = i_tokenContract.balanceOf(address(this));
will return “1” and will pass the line 126 if check if (tokenBalance > 0)
.
function will make the i_tokenContract.safeTransfer(i_seller, tokenBalance);
call.
safeTransfer
will call the tokensReceived()
function on the seller contract.
tokensReceived()
function will revert on seller contract reverting all the transaction.
Funds will be stuck on the Escrow contract as long as the seller keeps on front running arbiters transaction to resolveDispute.
This vulnerability has serious impact for the buyer and to some degree the arbiter. Buyer will not be able to get their funds back and the arbiter will not be able to get their arbiterFee having only wasted gas on a transaction that will revert. Seller can hold the funds hostage and blackmail the buyer into receiving part of the funds in order to let the transaction go through by stopping their front running attack.
Manual review.
Use Pull over Push.
Instead of resolveDispute function handling the transfers it should update a balances mapping for buyer, seller and arbiter. For example balances[buyer] = buyerAward, balances[arbiter] = arbiterFee, balances[seller] = (price - buyerAward) - arbiterFee
. We can implement a withdraw function such as:
When the buyer wants to get their funds back after arbiter calls the resolveDispute function, buyer and arbiter can call the withdraw function without worrying about a malicious seller.
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.