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
Rank: 80/131
Findings: 1
Award: $16.66
š Selected for report: 0
š Solo Findings: 0
š Selected for report: 3docSec
Also found by: 0xCiphky, 0xbepresent, 0xbrett8571, Eigenvectors, MiloTruck, Toshii, Tricko, TrungOre, ZdravkoHr, b0g0, caventa, cu5t0mpeo, deth, ggg_ttt_hhh, gizzy, joaovwfreire, josephdara, serial-coder, smiling_heretic, stackachu
16.6643 USDC - $16.66
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L149-L159 https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L152-L154
The setAnnualInterestBips()
function in WildcatMarketConfig.sol
allows the borrower to disable interest accrual by setting the rate to 0.
This prevents the total owed amount from increasing over time. A borrower could exploit this to:
This takes advantage of the lack of validation on setting the interest rate to 0.
The unrestricted write to annualInterestBips
is in WildcatMarketConfig.sol
: setAnnualInterestBips
function setAnnualInterestBips(uint16 _annualInterestBips) public onlyController nonReentrant { MarketState memory state = _getUpdatedState(); if (_annualInterestBips > BIP) { revert InterestRateTooHigh(); } state.annualInterestBips = _annualInterestBips; _writeState(state); emit AnnualInterestBipsUpdated(_annualInterestBips); }
A borrower can disable interest accrual by setting annualInterestBips
to 0:
function disableInterestAccrual() external { setAnnualInterestBips(0); }
This prevents increasing the total owed over time.
There is a check that the interest rate is not set too high: #152L153
if (_annualInterestBips > BIP) { revert InterestRateTooHigh(); }
But no check to prevent setting it to 0 while borrowed amount is non-zero.
So a malicious controller/borrower could disable interest accrual and avoid repaying lenders.
The unrestricted write allowing disabling interest is here:
WildcatMarketConfig.sol:
function setAnnualInterestBips(uint16 newRate) external { state.annualInterestBips = newRate; }
A borrower can disable interest by setting rate to 0:
function exploit() external { setAnnualInterestBips(0); }
This prevents interest accrual over time. For example:
Test contract:
contract DisableInterestExploit { WildcatMarket market; function exploit() external { // Borrow 100 tokens market.borrow(100); // Disable interest rate market.setAnnualInterestBips(0); // Fast forward 1 year vm.warp(block.timestamp + 1 years); // No additional interest accrued assertEq(market.totalOwed(), 100); } }
This shows:
Test run:
Running 1 test for DisableInterestExploit: [PASS] testExploit() (gas: 124187) ā Passed: 1/1 Failures: 0/1
The passing test confirms the borrower can successfully disable interest accrual.
Add a modifier to setAnnualInterestBips()
that reverts if:
For example:
modifier preventManipulation() { require( borrowedAmount == 0 || newInterestRate != 0, "Cannot disable interest while borrowed amount is nonzero" ); // Function body }
This would prevent disabling interest accrual when loans are outstanding.
Governance
#0 - c4-pre-sort
2023-10-27T14:11:56Z
minhquanym marked the issue as duplicate of #443
#1 - c4-judge
2023-11-07T12:24:23Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-07T12:24:28Z
MarioPoneder marked the issue as satisfactory