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.
A user creates a Pool.
setPool with lower pool balance.
so the contract sends remaining tokens to p.lender.
as the pool isn't updated.
pool.lender reenter and drain a contract from pool.loadToken.
uint256 currentBalance = pools[poolId].poolBalance;
if (p.poolBalance > currentBalance) {
IERC20(p.loanToken).transferFrom(
p.lender,
address(this),
p.poolBalance - currentBalance
);
} else if (p.poolBalance < currentBalance) {
IERC20(p.loanToken).transfer(
p.lender,
currentBalance - p.poolBalance
);
}
emit PoolBalanceUpdated(poolId, p.poolBalance);
if (pools[poolId].lender == address(0)) {
emit PoolCreated(poolId, p);
} else {
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;
}