Moonwell

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

Inaccurate Tracking of Bad Debt Due to Lack of Duplicate Prevention in fixUser

Summary

The fixUser function in the MErc20DelegateFixer contract does not incorporate checks to prevent duplicate invocations for the same user, potentially leading to inconsistent financial states within the platform. This oversight could result in the artificial inflation of the badDebt variable without an actual increase in the user's borrow balance, inaccurately reflecting the platform's financial health.

Vulnerability Details

The fixUser function is designed to zero out a user's borrow balance and transfer their collateral to a liquidator, subsequently increasing the badDebt counter by the user's principal amount. However, the function lacks a mechanism to prevent repeated executions for the same user when their borrow balance remains unchanged (i.e., already zeroed out by a previous call). This could allow for multiple increases in badDebt without actual debt being liquidated, if fixUser is called more than once for the same user under the same conditions.

Poc

Here's an example scenario to illustrate the vulnerability:

  • Alice is a user on the lending platform, and she has borrowed 100 tokens from the platform. Due to market conditions, Alice's collateral has been liquidated, and the fixUser function is called to handle her case.

  • The fixUser function correctly zeroes out Alice's borrow balance and transfers her collateral to the liquidator. It also increases the badDebt counter by 100 tokens, reflecting Alice's principal amount.

  • However, due to the lack of a duplicate prevention mechanism in the fixUser function, it is possible for the function to be called again for Alice, even though her borrow balance is already zero.

  • If fixUser is called a second time for Alice, it will execute its logic again, even though Alice's borrow balance is already zeroed out. This will result in another increase of 100 tokens to the badDebt counter, despite no new debt being liquidated.

  • The scenario can be repeated multiple times, leading to a significant inflation of the badDebt metric without an actual increase in the platform's debt. This could provide an inaccurate representation of the platform's financial health and potentially be exploited to manipulate financial metrics.

  • For example, if fixUser is called 10 times for Alice, the badDebt counter would increase by 1000 tokens, even though Alice's actual debt was only 100 tokens.

Poc code

poc.py

class User:
def __init__(self, name, borrow_balance):
self.name = name
self.borrow_balance = borrow_balance
self.collateral = 100 # Assuming some collateral for the user
class LendingPlatform:
def __init__(self):
self.bad_debt = 0
self.users = {}
self.liquidators = {}
def add_user(self, user):
self.users[user.name] = user
def add_liquidator(self, liquidator):
self.liquidators[liquidator.name] = liquidator
def fix_user_vulnerable(self, liquidator, user):
if user.borrow_balance != 0:
print(f"Fixing user {user.name} with borrow balance {user.borrow_balance}")
self.bad_debt += user.borrow_balance
user.borrow_balance = 0
liquidator.collateral += user.collateral
user.collateral = 0
print(f"Bad debt increased to {self.bad_debt}")
else:
print(f"User {user.name} already fixed, but bad debt still increased")
self.bad_debt += 100 # Incorrectly increasing bad_debt by a fixed amount
print(f"Bad debt increased to {self.bad_debt}")
def fix_user_mitigated(self, liquidator, user):
if user.borrow_balance != 0:
print(f"Fixing user {user.name} with borrow balance {user.borrow_balance}")
self.bad_debt += user.borrow_balance
user.borrow_balance = 0
liquidator.collateral += user.collateral
user.collateral = 0
print(f"Bad debt increased to {self.bad_debt}")
else:
print(f"User {user.name} already fixed, skipping")
# Create a lending platform instance
platform = LendingPlatform()
# Create a user with a borrow balance of 100
alice = User("Alice", 100)
platform.add_user(alice)
# Create a liquidator
liquidator = User("Liquidator", 0)
platform.add_liquidator(liquidator)
print("\nVulnerable fixUser function:")
# Fix Alice's account
platform.fix_user_vulnerable(liquidator, alice)
# Fix Alice's account again (exploit)
platform.fix_user_vulnerable(liquidator, alice)
# Fix Alice's account multiple times (exploit)
for _ in range(3):
platform.fix_user_vulnerable(liquidator, alice)
# Reset bad debt
platform.bad_debt = 0
print("\nMitigated fixUser function:")
# Fix Alice's account
platform.fix_user_mitigated(liquidator, alice)
# Try to fix Alice's account again (should be skipped)
platform.fix_user_mitigated(liquidator, alice)

Output

Vulnerable fixUser function:
Fixing user Alice with borrow balance 100
Bad debt increased to 100
User Alice already fixed, but bad debt still increased
Bad debt increased to 200
User Alice already fixed, but bad debt still increased
Bad debt increased to 300
User Alice already fixed, but bad debt still increased
Bad debt increased to 400
User Alice already fixed, but bad debt still increased
Bad debt increased to 500
Mitigated fixUser function:
User Alice already fixed, skipping
User Alice already fixed, skipping

Impact

Repeated calls to fixUser for the same user without changes in the user's borrow balance could:

  • Inflate the badDebt metric, undermining the accuracy of the platform's financial reporting.

  • Potentially exploit the system by artificially manipulating financial metrics.

  • Affect the platform's risk assessments and financial stability.

Tools Used

Manual Review, python

Recommendations

Introduce a check at the beginning of the fixUser function to ensure that it only proceeds if the user's current borrow balance (accountBorrows[user].principal) is non-zero. This prevents the function from executing its logic (and thus affecting the badDebt counter) if the user has already been fixed and their borrow balance is zero.

```solidity
require(accountBorrows[user].principal != 0, "user already fixed");
```
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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