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: 66/131
Findings: 4
Award: $29.95
🌟 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
Market can not be closed because it contains onlyController modifier but the controller does not have any function to close market. This makes the borrower to always pay interest with having any way to close market when he has repaid everything he owed.
The close function contains the onlyController modifier but through out the code of WildcatMarketController there is not function to closed market making it always open and infinite interest being paid even after all repayment
Mauel review
it should probably be onlyBorrower or a function is introduced in the controller and passes through
Invalid Validation
#0 - c4-pre-sort
2023-10-28T15:39:08Z
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:10:25Z
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: 0xStalin
Also found by: 0xCiphky, 0xComfyCat, 0xbepresent, 3docSec, Eigenvectors, Fulum, HChang26, Infect3d, QiuhaoLi, SandNallani, SovaSlava, TrungOre, YusSecurity, ZdravkoHr, ast3ros, ayden, bdmcbri, cu5t0mpeo, elprofesor, gizzy, jasonxiale, kodyvim, marqymarq10, max10afternoon, nisedo, nobody2018, radev_sw, rvierdiiev, serial-coder, xeros
6.5602 USDC - $6.56
Any other person can withdraw from a market which breaks protocol Invariants which states that
The assets of a market should never be able to be withdrawn by anyone that is not the borrower or a lender with either the WithdrawOnly or DepositAndWithdraw role. Exceptions: balances being transferred to a blocked account's escrow contract and collection of protocol fees.
This is possible because the lender can transfer his market tokens to any address and update his role to withdraw only
The updateLenderAuthorization is callable by any one which gives any specified address the withdrawOnly role if the address is not added by the borrower. So this makes it possible for anyone to withdraw from market .
test in test/market/wildcatmarketwithdrawals.t.sol
function test_queueWithdrawal_AuthorizedWithdrawOnly() public asAccount(bob) { address[] memory markets = new address[](1); markets[0] = address(market); _deposit(bob, 1e18); market.transfer(address(2), 1e18); startPrank(address(2)); controller.updateLenderAuthorization(address(2), markets); market.queueWithdrawal(1e18); stopPrank(); }
manuel review
make the function restricted to only borrower.
Access Control
#0 - c4-pre-sort
2023-10-28T16:16:44Z
minhquanym marked the issue as duplicate of #155
#1 - c4-judge
2023-11-07T12:55:22Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-07T12:58:50Z
MarioPoneder marked the issue as satisfactory
#3 - c4-judge
2023-11-14T13:59:00Z
MarioPoneder changed the severity to 3 (High Risk)
#4 - c4-judge
2023-11-14T14:00:00Z
MarioPoneder marked the issue as partial-50
#5 - c4-judge
2023-11-14T14:02:39Z
MarioPoneder marked the issue as duplicate of #266
🌟 Selected for report: YusSecurity
Also found by: 0xAsen, 0xCiphky, 0xDING99YA, 0xKbl, 0xSwahili, 0xbepresent, 3docSec, AS, Aymen0909, DeFiHackLabs, GREY-HAWK-REACH, KeyKiril, MiloTruck, QiuhaoLi, Silvermist, SovaSlava, TrungOre, VAD37, Vagner, Yanchuan, ZdravkoHr, ast3ros, cartlex_, d3e4, deth, ggg_ttt_hhh, gizzy, kodyvim, nirlin, nobody2018, rvierdiiev, serial-coder, sl1, tallo, xeros
6.6715 USDC - $6.67
there is a wrong placement of address when creating escrow which will return funds to borrower. when creating ecrow its first input should be the borrowers address and then then account for escrow but this was misplaced which will lead to the borrowers being set as account and so can withdraw all funds sent to escrow
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatSanctionsSentinel.sol#L95C2-L99C55 from the creating of escrow above you will find out that the borrowers address comes first before the account and this is exactly how it is recorded is ecrow. The problem is the blockaccount https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L172C7-L176C11 places the borrower as the second input which makes the borrower the account and the account the borrower meaning that the borrower can easily claim all funds in the escrow .
From test/market/wildcatmarketconfig.t.sol compare the alice address which should be the escrow onwer to which funds are sent to and the actual value stored as account in escrow which is the borrower instead of the alice
function test_nukeFromOrbit_WithBalance() external { _deposit(alice, 1e18); address escrow = sanctionsSentinel.getEscrowAddress( alice, borrower, address(market) ); sanctionsSentinel.sanction(alice); vm.expectEmit(address(market)); emit AuthorizationStatusUpdated(alice, AuthRole.Blocked); vm.expectEmit(address(market)); emit Transfer(alice, escrow, 1e18); vm.expectEmit(address(market)); emit SanctionedAccountAssetsSentToEscrow(alice, escrow, 1e18); market.nukeFromOrbit(alice); console2.log( "account address", WildcatSanctionsEscrow(escrow).account(), alice, borrower ); assertEq( uint(market.getAccountRole(alice)), uint(AuthRole.Blocked), "account role should be Blocked" ); }
Manuel review
just swap the addresses .
Error
#0 - c4-pre-sort
2023-10-28T14:34:14Z
minhquanym marked the issue as duplicate of #515
#1 - c4-judge
2023-11-07T12:12:06Z
MarioPoneder marked the issue as satisfactory
🌟 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
The setAnnualInterestBips is used by borrower to reset the interest rate he pays to any other of his choice but since the protocol implements a constraint on the interest the borrower choice must be within the min and max AnnualInterestBips but this is only checked during creation of market , a borrower can reset his AnnualInterestBips to 0% using setAnnualInterestBips in WildcatMarketController even when the min AnnualInterestBips set by protocol is above 0 or if the max AnnualInterestBips is less than the BIPs
the market only checks if it higher than the BIPs and not max AnnualInterestBips and does not check of it is lower than minAnnualInterestBips
Manuel review
Always check if the new AnnualInterestBips is less or higher than min and max AnnualInterestBips
Invalid Validation
#0 - c4-pre-sort
2023-10-27T14:24:02Z
minhquanym marked the issue as duplicate of #443
#1 - c4-judge
2023-11-07T12:33:13Z
MarioPoneder marked the issue as satisfactory