Assessment reports>Bracket Fi Escrow>Critical findings>Reentrancy in withdrawals
Category: Business Logic

Reentrancy in withdrawals

Critical Severity
Critical Impact
High Likelihood

Description

The function withdraw is responsible for the withdrawals of tokens from the escrow contract. The function first checks if there is enough balance in the usersBalance for the caller, then unwraps the token if unwrap is true, and finally decreases the amount from the usersBalance mapping and totalStaked.

If the rebase token is ETH_ADDRESS, the function will unwrap WETH to ETH and send the tokens to the user via the msg.sender.call{value: amount}(""); call. In this function, the checks-effects-interactions pattern is broken, as the interaction (i.e., the transfer of the amount to the caller) is happening before the effects (i.e., the deduction of the amount). Furthermore, the amount is deducted in an unchecked block, which leads to underflow in the balance, and thus the function does not revert when the amount subtracted is more than the balance.

function withdraw(address token, uint256 amount, bool unwrap) external onlyNotBroke returns (uint256) {
    if (amount == 0) revert ZeroAmount();

    EscrowBaseStorage storage s = _getStorage();

    uint256 balance = s.usersBalance[msg.sender][token];
    if (amount > balance) revert NotEnoughAmountStaked(balance); //[1] <- checks

    uint256 finalAmt = amount;
    if (unwrap) {
        //...
        } else if (tokenInfo.rebase == ETH_ADDRESS) {
            _unwrapETH(token, amount);
            (bool success,) = msg.sender.call{value: amount}(""); //[2] <- interaction
            if (!success) revert ETHSendFailed();

    //...
    unchecked {
        s.usersBalance[msg.sender][token] = balance - amount; //[3] <- effects
        s.tokens[token].totalStaked -= amount;
    }
    //...
}

Impact

An attacker could drain the WETH tokens available in the contract.

Recommendations

We recommend following the checks-effects-interactions pattern or adding a nonReentrant modifier to stop the reentrancy in this function.

Remediation

This issue has been acknowledged by Bracket Labs Group SA, and a fix was implemented in commit 6b7b4b1b.

Bracket Labs Group SA provided the following response:

Unchecking the balance subtraction was introduced while debugging an issue with rebase tokens and the development team forgot to remove it before the audit commit, and was originally meant to revert upon reentrancy. This was fixed immediately after the beginning of the audit, and nonReentrant modifier was added as an additional safeguard mechanism.

Zellic © 2024Back to top ↑