Moonwell

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

Inaccurate debt liquidations

Summary

The proposal implementation lacks explicit validation for debtor information read from JSON files before proceeding with operations such as liquidating debts or reallocating assets. This lack of data validation can introduce several risks to the protocol, including executing transactions with invalid data that could lead to failed transactions or unintended alterations in the protocol state.

Vulnerability Details

The contract reads debtor information from JSON files and uses this data to iterate over arrays of accounts for debt liquidation without performing any explicit validation on the data. Specifically, the implementation does not check for:

  • Non-zero addresses, ensuring that operations are only performed on valid Ethereum addresses.

  • The accuracy of associated debt amounts, which could lead to incorrect liquidation amounts or failing to liquidate debts as intended.

  • Duplicate entries, which could result in repeated operations for the same account, leading to inefficiencies or errors.

Code

File: mip-m17.sol
function _validate(Addresses addresses, address) internal override {
/// @dev check debtors have had their debt zeroed
{
string memory debtorsRaw = string(
abi.encodePacked(
vm.readFile("./src/proposals/mips/mip-m17/mFRAX.json")
)
);
bytes memory debtorsParsed = vm.parseJson(debtorsRaw);
Accounts[] memory debtors = abi.decode(debtorsParsed, (Accounts[]));
IMErc20Delegator mErc20Delegator = IMErc20Delegator(
addresses.getAddress("MOONWELL_mFRAX")
);
assertTrue(
mErc20Delegator.badDebt() != 0,
"mFRAX bad debt should not start at 0"
);
Comptroller comptroller = Comptroller(
addresses.getAddress("UNITROLLER")
);
for (uint256 i = 0; i < debtors.length; i++) {
(uint256 err, , ) = comptroller.getAccountLiquidity(
debtors[i].addr
);
assertEq(
err,
0,
string(
abi.encodePacked(
"error code getting liquidity for account: ",
Strings.toHexString(debtors[i].addr)
)
)
);
assertEq(
mErc20Delegator.borrowBalanceStored(debtors[i].addr),
0,
"mfrax borrow balance after seizing not zero"
);
assertEq(
mErc20Delegator.balanceOf(debtors[i].addr),
0,
"mfrax balance after seizing"
);
}
}
{
string memory debtorsRaw = string(
abi.encodePacked(
vm.readFile("./src/proposals/mips/mip-m17/mxcDOT.json")
)
);
bytes memory debtorsParsed = vm.parseJson(debtorsRaw);
Accounts[] memory debtors = abi.decode(debtorsParsed, (Accounts[]));
IMErc20Delegator mErc20Delegator = IMErc20Delegator(
addresses.getAddress("MOONWELL_mxcDOT")
);
for (uint256 i = 0; i < debtors.length; i++) {
assertEq(
mErc20Delegator.balanceOf(debtors[i].addr),
0,
"mxcDOT balanceOf after seizing not zero"
);
assertEq(
mErc20Delegator.borrowBalanceStored(debtors[i].addr),
0,
"mxcDOT borrow balance after seizing not zero"
);
}
}
IMErc20Delegator mUSDCMErc20Delegator = IMErc20Delegator(
addresses.getAddress("MOONWELL_mUSDC")
);
IMErc20Delegator mETHMErc20Delegator = IMErc20Delegator(
addresses.getAddress("MOONWELL_mETH")
);
IMErc20Delegator mwBTCMErc20Delegator = IMErc20Delegator(
addresses.getAddress("MOONWELL_mwBTC")
);
address reallocationMultisig = addresses.getAddress(
"NOMAD_REALLOCATION_MULTISIG"
);
/// @dev check that the Nomad assets have been swept
assertEq(
IERC20(addresses.getAddress("madUSDC")).balanceOf(
reallocationMultisig
),
mUSDCCash,
"mad usdc msig balance incorrect"
);
assertEq(mUSDCMErc20Delegator.getCash(), 0, "mad usdc cash incorrect");
assertEq(
mUSDCMErc20Delegator.balanceOf(reallocationMultisig),
0,
"musdc balance of msig incorrect"
);
assertEq(
IERC20(addresses.getAddress("madWETH")).balanceOf(
reallocationMultisig
),
mETHCash,
"mad eth msig balance incorrect"
);
assertEq(
mETHMErc20Delegator.balanceOf(reallocationMultisig),
0,
"meth balance of msig incorrect"
);
assertEq(mETHMErc20Delegator.getCash(), 0, "mad eth cash incorrect");
assertEq(
IERC20(addresses.getAddress("madWBTC")).balanceOf(
reallocationMultisig
),
mwBTCCash,
"mad btc msig balance incorrect"
);
assertEq(
mwBTCMErc20Delegator.balanceOf(reallocationMultisig),
0,
"mwbtc balance of msig incorrect"
);
assertEq(mwBTCMErc20Delegator.getCash(), 0, "mad btc cash incorrect");
}

Impact

  • Executing operations with incorrect data can lead to unintended changes in the protocol state, such as inaccurate debt liquidations, which could affect the protocol's solvency or users' positions.

  • Transactions targeting zero addresses fail, wasting gas and potentially causing the entire liquidation process to halt or behave unpredictably.

Poc

- The contract is deployed with functionalities to liquidate bad debt based on debtor information parsed from external JSON files.
- Two JSON files exist: `mFRAX.json` and `mxcDOT.json`, each supposed to contain addresses with outstanding debt.
- A Malicious admin with the ability to influence the content of `mFRAX.json` can edit this file.
- The Malicious admin/insider injects an invalid or malicious entry into `mFRAX.json`. This could be a zero address, an address the attacker controls without any debt, or an address with specially crafted conditions.
- The contract reads the manipulated `mFRAX.json` file and proceeds to attempt liquidation on the basis of the provided data without validation.
- Since there is no validation for zero addresses or verification of actual debt, the contract attempts to liquidate the debt for the malicious/invalid entries.
  1. Potential Outcomes:

    • Failed Transactions: If the injected address is invalid (e.g., zero address), the liquidation process may fail, wasting gas and potentially causing legitimate liquidation processes to be delayed or missed.

    • Unintended Liquidation: If the address has no actual debt but is manipulated to appear as if it does, the contract may perform unnecessary operations, again wasting gas and potentially disrupting the protocol's intended behavior.

    • Depending on the attacker's intentions and the contract's logic, injecting malicious addresses could expose the contract or its users to further vulnerabilities.

Tools Used

Manual Review

Recommendations

  • Before processing the debtor information, implement checks to validate the integrity of the data, such as verifying non-zero addresses and the correctness of debt amounts.

  • Ensure that the debtor lists are deduplicated to prevent repeated operations for the same account, which can save gas and reduce the risk of errors.

Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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