20,000 USDC
View results
Submission Details
Severity: high
Valid

Reentrancy in setPool in Lender.sol

Summary

setPool function in Lender.sol file, doesn't follow the CEI pattern and updates pools after an external call.

Vulnerability Details

The implementation does not strictly follow the Checks-Effects-Interactions (CEI) pattern.
Malicious attackers and unsuspecting ecosystem participants can add a pool with ERC-777 tokens (which have
a callback that can take control) and exploit this vulnerability.
A malicious user can drain a contract from pool.loadToken with the following steps.

  1. A user creates a Pool.

  2. setPool with lower pool balance.

  3. so the contract sends remaining tokens to p.lender.

  4. as the pool isn't updated.

  5. pool.lender reenter and drain a contract from pool.loadToken.

uint256 currentBalance = pools[poolId].poolBalance;
if (p.poolBalance > currentBalance) {
// if new balance > current balance then transfer the difference from the lender
IERC20(p.loanToken).transferFrom(
p.lender,
address(this),
p.poolBalance - currentBalance
);
} else if (p.poolBalance < currentBalance) {
// if new balance < current balance then transfer the difference back to the lender
IERC20(p.loanToken).transfer(
p.lender,
currentBalance - p.poolBalance
);
}
emit PoolBalanceUpdated(poolId, p.poolBalance);
if (pools[poolId].lender == address(0)) {
// if the pool doesn't exist then create it
emit PoolCreated(poolId, p);
} else {
// if the pool does exist then update it
emit PoolUpdated(poolId, p);
}
pools[poolId] = p;

Impact

drain a contract and steel all pool.loadToken from contract

Tools Used

manual review

Recommendations

follow CEI pattern and update state changes before external call.

uint256 currentBalance = pools[poolId].poolBalance;
+ if (pools[poolId].lender == address(0)) {
+ // if the pool doesn't exist then create it
+ emit PoolCreated(poolId, p);
+ } else {
+ // if the pool does exist then update it
+ emit PoolUpdated(poolId, p);
+ }
+ pools[poolId] = p;
if (p.poolBalance > currentBalance) {
// if new balance > current balance then transfer the difference from the lender
IERC20(p.loanToken).transferFrom(
@@ -164,15 +172,7 @@ contract Lender is Ownable {
emit PoolBalanceUpdated(poolId, p.poolBalance);
- if (pools[poolId].lender == address(0)) {
- // if the pool doesn't exist then create it
- emit PoolCreated(poolId, p);
- } else {
- // if the pool does exist then update it
- emit PoolUpdated(poolId, p);
- }
- pools[poolId] = p;
}

Support

FAQs

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