40,000 USDC
View results
Submission Details
Severity: high

Seller can DoS resolveDispute function

Summary

A malicious seller contract can disrupt the resolveDispute function.

Vulnerability Details

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:

  1. Buyer created the new escrow contract with the token contract being an ERC777 or any other call on transfer token.

  2. Seller refuses to do the work buyer asked for with the agreed price.

  3. Buyer calls the initiateDispute function to get their funds back.

  4. Arbiter calls the resolveDispute function with the buyerAward parameter being balanceOf(contract) - arbiterFee expecting the funds minus the arbiterFee going back to the buyer.

  5. Seller’s bot seeing this transcation call makes a front running attack to send 1 token to the contract.

  6. 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) .

  7. function will make the i_tokenContract.safeTransfer(i_seller, tokenBalance); call.

  8. safeTransfer will call the tokensReceived() function on the seller contract.

  9. tokensReceived() function will revert on seller contract reverting all the transaction.

  10. Funds will be stuck on the Escrow contract as long as the seller keeps on front running arbiters transaction to resolveDispute.

Impact

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.

Tools Used

Manual review.

Recommendations

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:

function withdraw() inState(State.Resolved){
require(balance[msg.sender] > 0, "You don't have any balance");
uint balance = balance[msg.sender];
balance[msg.sender] = 0;
i_tokenContract.safeTransfer(msg.sender,balance);
}

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.