Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect use of state.isDelinquent in updateTimeDelinquentAndGetPenaltyTime function - total misbehavior of applying delinquency fee #18

Closed
c4-bot-9 opened this issue Sep 18, 2024 · 3 comments
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_17_group AI based duplicate group recommendation sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-bot-9
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/libraries/FeeMath.sol#L96
https://github.com/code-423n4/2024-08-wildcat/blob/main/src/market/WildcatMarketBase.sol#L406

Vulnerability details

Impact

Due to the incorrect value of state.isDelinquent, the updateTimeDelinquentAndGetPenaltyTime function malfunctions, causing significant disruptions in fee accumulation.

Proof of Concept

Over time, as market operations occur, borrowers accumulate fees. This process is managed by the FeeMath.updateScaleFactorAndFees function. While this function updates the scaleFactor and applies the protocolFee, it also implements the delinquency fee based on the market's delinquency status. The calculation is performed by the updateDelinquency function, which then utilizes the updateTimeDelinquentAndGetPenaltyTime function to determine the duration for which the delinquencyFeeBips should be applied.

This function has a critical flaw by using incorrect state.isDelinquent value. It's assumed that:

  1. state.isDelinquent is up-to-update
  2. The delinqency state of market has been consistent (state.isDelinquent) since last update until current timestamp.
  function updateTimeDelinquentAndGetPenaltyTime(
    MarketState memory state,
    uint256 delinquencyGracePeriod,
    uint256 timeDelta
  ) internal pure returns (uint256 /* timeWithPenalty */) {
    // Seconds in delinquency at last update
    uint256 previousTimeDelinquent = state.timeDelinquent;

>>   if (state.isDelinquent) {

    ...
  }

However, this is WRONG because

  • state.isDelinquent is the delinquency status of last updated time (indicated by state.lastInterestAccruedTimestamp)
  • It is only updated when calling _writeState function which is normally at the end of an action.

So the whole deliquent penalty time is calculated wrongly, which leads to incorrect accumulation of delinquency fee.

Tools Used

Manual Review

Recommended Mitigation Steps

Though we can't completely guarantee 2nd point (The delinqency state of market has been consistent) because delinquency state can be changed in-between by direct transfer-in of assets (either by borrower or whoever), we can mostly assume it's true for a specific duration between market operations.

We can have correct value of state.isDelinquent, so that updateTimeDelinquentAndGetPenaltyTime function could perform correctly. It can be done by making changes to _getUpdatedState function of WildcatMarketBase contract.

  function _getUpdatedState() internal returns (MarketState memory state) {
    state = _state;
+   bool isDelinquent = state.liquidityRequired() > totalAssets();
+   state.isDelinquent = isDelinquent;
    ...
  }

Assessed type

Error

@c4-bot-9 c4-bot-9 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Sep 18, 2024
c4-bot-9 added a commit that referenced this issue Sep 18, 2024
@c4-bot-11 c4-bot-11 added the 🤖_17_group AI based duplicate group recommendation label Sep 18, 2024
@c4-bot-13 c4-bot-13 added the 🤖_primary AI based primary recommendation label Sep 18, 2024
@howlbot-integration howlbot-integration bot added primary issue Highest quality submission among a set of duplicates sufficient quality report This report is of sufficient quality labels Sep 20, 2024
@d1ll0n
Copy link

d1ll0n commented Sep 20, 2024

The reason we record the delinquency status at the end of a transaction and then accrue time delinquent at the beginning of the next is to ensure the delinquency timer always increases by at least the amount of time the market was actually delinquent.

It is technically possible for a market to become healthy again without being updated, but there are only three ways a market can go from delinquent to not delinquent:

  • A direct transfer, which no one has any incentive to do and which we can't react to since ERC20 does not have any kind of hooks
  • A deposit, which will update the state
  • A repay, which will update the state

If it worked the way you're proposing, a borrower could negate a long period of delinquency simply by transferring tokens in at the end and then updating the state. This is certainly not a high, it's more of an opinion on who should bear the burden of updating the state: the lender or the borrower. If we went with the lender, they would have to be expected to constantly poke the state whenever a borrower is delinquent, lest they come along at the last second, cure any delinquency and incur no penalties. On the other hand, the borrower bearing this burden of updating the state when it is in their favor simply means they should use the repay function, which they would almost certainly be doing every time they cure delinquency regardless.

@d1ll0n d1ll0n added the sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue label Sep 20, 2024
@3docSec
Copy link

3docSec commented Oct 3, 2024

If I am not mistaken, this finding was discussed extensively here code-423n4/2023-10-wildcat-findings#323 and at this point it's quite clear it's a deliberate design choice.

@c4-judge c4-judge closed this as completed Oct 3, 2024
@c4-judge
Copy link
Contributor

c4-judge commented Oct 3, 2024

3docSec marked the issue as unsatisfactory:
Invalid

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Oct 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_17_group AI based duplicate group recommendation sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

6 participants