Moonwell

Moonwell
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: low
Invalid

Frontrunning attack on `fixUser()` function can lead to user pay all bad Debt with nearly zero Amount of Token

Summary

In the contract MErc20DelegateFixer.sol , the function fixUser() which allows admin/governor to fix a user via transfering all it tokens to liquidator and repay its bad debt . Note , admin will call this function only when admin realises that the user have enough tokens , which would be able to repay the principle amount of debt . Once admin calls this function , any mallicious user will wait this call to happen and front run it with transfering all its token somewhere else and left by with minimal or 1 token , since there is no check which ensures accountTokens[user] >= principal , The mallicious user will able to repay all its debt with almost no amount of token .

Vulnerability Details

In the function below

function fixUser(address liquidator, address user) external {
/// @dev check user is admin
require(msg.sender == admin, "only the admin may call fixUser");
/// ensure nothing strange can happen with incorrect liquidator
require(liquidator != user, "liquidator cannot be user");
require(accrueInterest() == 0, "accrue interest failed");
/// @dev fetch user's current borrow balance, first updating interest index
uint256 principal = borrowBalanceStored(user);
require(principal != 0, "cannot liquidate user without borrows");
/// user effects
/// @dev zero balance
accountBorrows[user].principal = 0;
accountBorrows[user].interestIndex = borrowIndex;
/// @dev current amount for a user that we'll transfer to the liquidator
uint256 liquidated = accountTokens[user];
/// can only seize collateral assets if they exist
if (liquidated != 0) {
/// if assets were liquidated, give them to the liquidator
accountTokens[liquidator] = SafeMath.add(
accountTokens[liquidator],
liquidated
);
/// zero out the user's tokens
delete accountTokens[user];
}
/// global effects
/// @dev increment the bad debt counter
badDebt = SafeMath.add(badDebt, principal);
/// @dev subtract the previous balance from the totalBorrows balance
totalBorrows = SafeMath.sub(totalBorrows, principal);
emit UserFixed(user, liquidator, liquidated);
}

Suppose Alice is a Mallicious user with bad debt such that,
principal = borrowBalanceStored(Alice) = 1000
&
accountTokens[Alice] = 1200

Admin realises that the bad debt of Alice can be paid since he hold more token then debt , so admin calls fixUser(liquidator, Alice)
Since Allice is a Mallicious user, it will wait for admin to call fixUser(liquidator,Alice) , once user calls this function , Alice will front run this call
by transferring most of its token lets say 1199 to a different account or somewhere else ,
such that remaining accountTokens[Alice] = 1 ;

thus liquidated = 1;

since liquidated !=0 , The function passes through with
accountBorrows[Alice].principal = 0;

Impact

High

Tools Used

Manual Review

Recommendations

+ require(liquidated > principal, "not enough tokens");
Updates

Lead Judging Commences

0xnevi Lead Judge
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-front-run-fixUser

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
Assigned finding tags:

finding-front-run-fixUser

Support

FAQs

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