The Wildcat Protocol - 0xAsen's results

Banking, but worse - a protocol for fixed-rate, undercollateralised credit facilities.

General Information

Platform: Code4rena

Start Date: 16/10/2023

Pot Size: $60,500 USDC

Total HM: 16

Participants: 131

Period: 10 days

Judge: 0xTheC0der

Total Solo HM: 3

Id: 296

League: ETH

Wildcat Protocol

Findings Distribution

Researcher Performance

Rank: 108/131

Findings: 1

Award: $3.34

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

3.3357 USDC - $3.34

Labels

bug
3 (High Risk)
partial-50
duplicate-68

External Links

Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketBase.sol#L163

Vulnerability details

Impact

In function _blockAccount in WildcatMarketBase.sol, the parameters for createEscrow are passed in the wrong order leading to unintended behaviour of the newly created Escrow contract and opening up a lot of issues.

Let's see the part of the _blockAccount function where we call createEscow:

 function _blockAccount(MarketState memory state, address accountAddress) internal {
  
      if (scaledBalance > 0) {
        account.scaledBalance = 0;
        address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow(
          accountAddress,
          borrower,
          address(this)
        );
  }

while the createEscrow function expects the parameters in the following order:

function createEscrow(
    address borrower,
    address account,
    address asset
  ) public override returns (address escrowContract) {}

Instead of borrower, we're passing the account and vice versa. This leads to multiple issues like the supposed blocked lender being able to release funds. The blockAccount function is used in nukeFromOrbit and results in not really being able to block someone.

Proof of Concept

Here is a POC that I added to WildcatMarketConfig.t.sol: You need to add this to the test file before that:

import { WildcatSanctionsEscrow, IWildcatSanctionsEscrow } from 'src/WildcatSanctionsEscrow.sol';

contract MockWildcatArchController {
  mapping(address market => bool) public isRegisteredMarket;

  function setIsRegisteredMarket(address market, bool isRegistered) external {
    isRegisteredMarket[market] = isRegistered;
  }
}

And the test:

   function test_nukeFromOrbitIssue() public {
    //deploying mock archController
    MockWildcatArchController archController;
    archController = new MockWildcatArchController();
    
    //deploying mock SanctionsSentinel
    sanctionsSentinel = new MockSanctionsSentinel(address(archController));
    
    //set this address as registered market
    archController.setIsRegisteredMarket(address(this), true);

    //deposit in market
    vm.prank(alice);
    market.deposit(1e18);

    //sanction alice
    sanctionsSentinel.sanction(alice);
      
    //nuke it and assert it's blocked
    market.nukeFromOrbit(alice);
    assertEq(
      uint(market.getAccountRole(alice)),
      uint(AuthRole.Blocked),
      'account role should be Blocked'
    );
    
    //create the escrow with the param order in blockAccount
    WildcatSanctionsEscrow escrow = WildcatSanctionsEscrow(
      sanctionsSentinel.createEscrow(alice, borrower, address(market))
    );

    //should be false as Alice is sanctioned by borrower
    //but returns true because what it checks actually is if borrower is sanctioned by Alice
    console.log("canReleaseEscrow: ", escrow.canReleaseEscrow());
  }

Tools Used

Manual review, Foundry

Pass the parameters correctly:

function createEscrow(address borrower, address account, address asset)

Assessed type

Error

#0 - c4-pre-sort

2023-10-27T02:31:40Z

minhquanym marked the issue as duplicate of #515

#1 - c4-judge

2023-11-07T11:57:21Z

MarioPoneder marked the issue as satisfactory

#2 - c4-judge

2023-11-07T11:57:43Z

MarioPoneder marked the issue as partial-50

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter