40,000 USDC
View results
Submission Details
Severity: medium

Seller's Potential to Exploit ERC777 Token Hooks Leading to Frozen Funds in Arbitration

Summary

The current design allows for potential exploitation when the token with a hook. This vulnerability emerges during the arbitration process because the contract attempts to send any unallocated funds to the seller. This setup inadvertently offers an opportunity for the seller to front-run, thereby ensuring the token's transfer hook is triggered. If the seller then revert in the hook, it blocks the transaction from being executed. This exploit not only undermines the functionality of the arbitration process, but also poses the risk of freezing the funds within the contract.

Vulnerability Details

https://github.com/Cyfrin/2023-07-escrow/blob/65a60eb0773803fa0be4ba72defaec7d8567bccc/src/Escrow.sol#L109-L129

function resolveDispute(uint256 buyerAward) external onlyArbiter nonReentrant inState(State.Disputed) {
uint256 tokenBalance = i_tokenContract.balanceOf(address(this));
uint256 totalFee = buyerAward + i_arbiterFee; // Reverts on overflow
if (totalFee > tokenBalance) {
revert Escrow__TotalFeeExceedsBalance(tokenBalance, totalFee);
}
s_state = State.Resolved;
emit Resolved(i_buyer, i_seller);
if (buyerAward > 0) {
i_tokenContract.safeTransfer(i_buyer, buyerAward);
}
if (i_arbiterFee > 0) {
i_tokenContract.safeTransfer(i_arbiter, i_arbiterFee);
}
tokenBalance = i_tokenContract.balanceOf(address(this));
if (tokenBalance > 0) {
i_tokenContract.safeTransfer(i_seller, tokenBalance);
}
}

Given:

  • Alice, a buyer who uses an ERC777 token.

  • Bob, a seller who has control over the token's hooks.

When:

  1. Alice deposits 1000 ERC777 tokens into the contract.

  2. A dispute arises and the arbitrator rules in favor of Alice. The arbitrator's decision triggers the contract to transfer all 1000 tokens back to Alice.

  3. Before the transfer transaction is mined, Bob performs a front-running attack. He transfers 1 wei of ERC777 tokens to the contract.

  4. Due to the transfer, Bob's tokensToSend hook is triggered. He then causes the tokensToSend hook to fail, which blocks the transfer of the 1000 tokens to Alice.

  5. The arbitrator's transaction is then mined, but the transfer fails due to Bob's earlier front-running transaction and hook manipulation.

  6. Alice expected to receive her 1000 ERC777 tokens after the dispute resolution but the transfer fails. Bob can keep doing this and make the funds remain frozen in the contract.

Impact

The current design could lead to locked funds within the contract if an ERC777 token (hook token) is used. This can occur when a dispute is raised and the arbitrator rules in favor of the buyer, but the seller uses the token hook to prevent the transaction from occurring, thus freezing the funds within the contract.

Tools Used

Auditor's brain & ChatGPT & VS Code

Recommendations

Consider modifying the contract to:

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

Add a function that allows the seller to withdraw funds when a transfer fails.

function sellerWithdrawAfterFailedTransfer(address recipient) external nonReentrant {
require(msg.sender == i_seller, "Only seller can call this function.");
i_tokenContract.safeTransfer(i_seller, i_seller_amount);
i_seller_amount = 0;
}

Support

FAQs

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