40,000 USDC
View results
Submission Details
Severity: high

EscrowFactory Reentrancy Attack through Salted Contract Creations

Summary

The EscrowFactory contract is vulnerable to a reentrancy attack through salted contract creations. The vulnerability arises due to the sequence of token transfer and contract deployment, allowing an attacker to exploit the salted contract creation mechanism to deploy a malicious contract at the same address as the legitimate Escrow contract. This can lead to unauthorized token transfers and potential financial losses.

Vulnerability Details

The vulnerability lies in the newEscrow function of the EscrowFactory contract. The function is responsible for creating new Escrow contracts and transferring tokens to the Escrow contract to hold in escrow. The process involves calculating the computedAddress based on the provided salt and other parameters using the computeEscrowAddress function. Subsequently, tokens are transferred from msg.sender to the computedAddress. Finally, the Escrow contract is deployed at the computedAddress.

function newEscrow(
uint256 price,
IERC20 tokenContract,
address seller,
address arbiter,
uint256 arbiterFee,
bytes32 salt
) external returns (IEscrow) {
address computedAddress = computeEscrowAddress(...);
// Vulnerable Code: Reentrancy Attack
tokenContract.safeTransferFrom(msg.sender, computedAddress, price);
// Deploying the Escrow contract
Escrow escrow = new Escrow{salt: salt}(...);
// ...
}

Impact

The reentrancy attack enabled by this vulnerability allows an attacker to repeatedly call the newEscrow function from their malicious Escrow contract, effectively reentering the EscrowFactory contract. This can lead to unauthorized token transfers from unsuspecting users, potential financial losses, and disruptions to the intended escrow process.

Tools Used

Manual

Recommendations

To mitigate this vulnerability, the token transfer should be moved after deploying the Escrow contract. By doing so, the computedAddress will be the address of the legitimate Escrow contract, and the token transfer will happen only after the contract has been deployed. This change ensures that the Escrow contract is fully set up before any external interactions occur, preventing the reentrancy attack.

function newEscrow(
uint256 price,
IERC20 tokenContract,
address seller,
address arbiter,
uint256 arbiterFee,
bytes32 salt
) external returns (IEscrow) {
address computedAddress = computeEscrowAddress(...);
Escrow escrow = new Escrow{salt: salt}(...);
if (address(escrow) != computedAddress) {
revert EscrowFactory__AddressesDiffer();
}
// Move the token transfer after deploying the Escrow contract
tokenContract.safeTransferFrom(msg.sender, address(escrow), price);
emit EscrowCreated(address(escrow), msg.sender, seller, arbiter);
return escrow;
}

Support

FAQs

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