20,000 USDC
View results
Submission Details
Severity: high

ERC777 reentrancy can be used to withdraw all tokens from the contract

Summary

Pools can be created with arbitrary tokens as the collateral, including ERC777 tokens. However, when the addToPool is used with those tokens, an attacker can drain all contract's tokens completely. This happens because with ERC777 tokens, there is a tokensToSend hook that is executed before the actual transfer (and the balance updates) happen.

Vulnerability Details

Lender.sol has an addToPool function, which allows the lender to add funds to the pool. However, this function doesn't follow CEI (checks, effects, interactions) pattern and lacks a reentrancy guard. Consequently, any user can reenter this function and increase their poolBalance.

Example:

  1. By using ERC777, an attacker can reenter addToPool function and increase their poolBalance

185: _updatePoolBalance(poolId, pools[poolId].poolBalance + amount);
  1. Then the attacker can call the setPool() function to change the collateral token, which this function doesn't prohibit. poolBalance will stay the same

  2. Finally, the attacker may call setPool() again and reduce their poolBalance.

157: } else if (p.poolBalance < currentBalance) {
158: // if new balance < current balance then transfer the difference back to the lender
159: IERC20(p.loanToken).transfer(
160: p.lender,
161: currentBalance - p.poolBalance
161: );
163: }

The attacker can repeat steps 2-3 with all collateral tokens

Impact

Malicious user can steal all collateral tokens

Tools Used

Manual Review

Recommendations

Add reentrancy guard to the addToPool function and change it in the following way:

function addToPool(bytes32 poolId, uint256 amount) external {
if (pools[poolId].lender != msg.sender) revert Unauthorized();
if (amount == 0) revert PoolConfig();
- _updatePoolBalance(poolId, pools[poolId].poolBalance + amount);
// transfer the loan tokens from the lender to the contract
IERC20(pools[poolId].loanToken).transferFrom(
msg.sender,
address(this),
amount
);
+ _updatePoolBalance(poolId, pools[poolId].poolBalance + amount);
}

Support

FAQs

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

Give us feedback!