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: 9/131
Findings: 3
Award: $664.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xpiken
Also found by: 0xCiphky, 0xComfyCat, 0xStalin, 0xhegel, 0xkazim, 3docSec, AM, Aymen0909, CaeraDenoir, DeFiHackLabs, Drynooo, Eigenvectors, Fulum, HALITUS, HChang26, Jiamin, Juntao, LokiThe5th, Mike_Bello90, MiloTruck, QiuhaoLi, Silvermist, SovaSlava, SpicyMeatball, T1MOH, Toshii, TrungOre, TuringConsulting, Vagner, Yanchuan, ZdravkoHr, _nd_koo, almurhasan, audityourcontracts, ayden, cartlex_, circlelooper, crunch, cu5t0mpeo, deth, erictee, ggg_ttt_hhh, gizzy, gumgumzum, hash, jasonxiale, josephdara, ke1caM, kodyvim, lanrebayode77, marqymarq10, max10afternoon, nirlin, nonseodion, osmanozdemir1, peter, radev_sw, rvierdiiev, said, serial-coder, sl1, smiling_heretic, squeaky_cactus, stackachu, tallo, trachev, zaevlad
0.0606 USDC - $0.06
The function closeMarket()
sets the APR to 0% and does the necessary fund transfers.
This function implements a modifier onlyController
, this requires that only the controller can make this call. However the controller contract has no method that calls the function closeMarket()
.
modifier onlyController() { if (msg.sender != controller) revert NotController(); _; } function closeMarket() external onlyController nonReentrant {}
In the Market Controller contract available at https://github.com/code-423n4/2023-10-wildcat/blob/main/src/WildcatMarketController.sol, there exists no function to call the closeMarket()
Manual Review
Add a function in the Market Controller contract that calls WildcatMarket.closeMarket()
Other
#0 - c4-pre-sort
2023-10-27T07:08:22Z
minhquanym marked the issue as duplicate of #147
#1 - c4-judge
2023-11-07T13:53:20Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-07T14:01:07Z
MarioPoneder marked the issue as partial-50
#3 - c4-judge
2023-11-07T14:16:53Z
MarioPoneder changed the severity to 3 (High Risk)
🌟 Selected for report: MiloTruck
Also found by: josephdara
647.6492 USDC - $647.65
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatSanctionsSentinel.sol#L100-L102 https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketWithdrawals.sol#L164-L170
Markets can be added and removed from the ArchController
by the controller and owner respectively.
Removal of a market from the ArchController is expected, however this creates an issue for sanctioned acccount.
Sanctioned accounts are unable to make a withdrawal because the function createEscrow
checks if the market is registered.
function createEscrow( address borrower, address account, address asset ) public override returns (address escrowContract) { if (!IWildcatArchController(archController).isRegisteredMarket(msg.sender)) { revert NotRegisteredMarket(); }
This causes a DOS for addresses with a sanction as the transaction would always revert and users will not get their funds. After a discussion with the sponsor, it was said that the check will be removed from the sanctions sentinel to keep it from affecting withdrawal.
Manual Review
As said by the sponsor, the check would be removed to allow santioned users access their funds.
Error
#0 - c4-pre-sort
2023-10-28T18:04:51Z
minhquanym marked the issue as duplicate of #498
#1 - c4-judge
2023-11-07T12:22:05Z
MarioPoneder marked the issue as not a duplicate
#2 - c4-judge
2023-11-08T17:01:05Z
MarioPoneder marked the issue as unsatisfactory: Insufficient proof
#3 - Josephdara
2023-11-13T19:50:47Z
Hello @MarioPoneder This issue is a duplicate of #498 because the core issue is the same. If a market is removed, sanctioned lenders will be unable to call executeWithdrawal() to withdraw their assets from the market into their own escrows.This is because an escrow account is created for the sanctioned users when they try to withdraw, however withdrawal reverts because of the check I highlighted above.
function createEscrow( address borrower, address account, address asset ) public override returns (address escrowContract) { if (!IWildcatArchController(archController).isRegisteredMarket(msg.sender)) { revert NotRegisteredMarket(); }
This causes a DOS for addresses with a sanction as the transaction would always revert and users will not get their funds.
I provided all this in the report above.
#4 - MarioPoneder
2023-11-14T22:09:10Z
Thank you for your comment!
In contrast to the primary issue, the present report has not identified all impacts and is lacking quality (level of detail) as well as proof.
Nevertheless, I acknowledge that the withdrawal DoS was identified and therefore willing to restore duplication with partial credit.
#5 - c4-judge
2023-11-14T22:09:23Z
MarioPoneder marked the issue as duplicate of #498
#6 - c4-judge
2023-11-14T22:09:44Z
MarioPoneder marked the issue as partial-50
🌟 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/WildcatMarketController.sol#L67-L68 https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatMarketController.sol#L468-L488 https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L149-L159
When markets are deployed using the Factory deployControllerAndMarket()
or with the controllerdeployMarket()
, a check is performed on the AnnualInterestBips
It is ensured that the APR is always less than maximumAnnualInterestBips
preset in the factory and greater than the MinimumAnnualInterestBips
However, a borrower can increase or reduce the market's APR to any value using the WildcatMarketController.setAnnualInterestBips()
It can be set to zero, or any amount greater than the allowed maximum from the Factory contract.
After a discussion with the spponsor, this was confirmed to be an issue.
The function setAnnualInterestBips()
does no checks to ensure the limiters on the new annualInterestBips
.
function setAnnualInterestBips( address market, uint16 annualInterestBips ) external virtual onlyBorrower onlyControlledMarket(market) { // If borrower is reducing the interest rate, increase the reserve // ratio for the next two weeks. if (annualInterestBips < WildcatMarket(market).annualInterestBips()) { TemporaryReserveRatio storage tmp = temporaryExcessReserveRatio[market]; if (tmp.expiry == 0) { tmp.reserveRatioBips = uint128(WildcatMarket(market).reserveRatioBips()); // Require 90% liquidity coverage for the next 2 weeks WildcatMarket(market).setReserveRatioBips(9000); } tmp.expiry = uint128(block.timestamp + 2 weeks); } WildcatMarket(market).setAnnualInterestBips(annualInterestBips); }
The sanity check is very important this point because there also is no check inside the market contract except the BIP value.
Manual Review
As discussed with the sponsor, the max and min values can be stored as an immutable in the market contract and checked directly in the WildCatMarket.setAnnualInterestBips()
function.
Math
#0 - c4-pre-sort
2023-10-27T14:14:27Z
minhquanym marked the issue as duplicate of #443
#1 - c4-judge
2023-11-07T12:26:02Z
MarioPoneder marked the issue as satisfactory