Platform: Code4rena
Start Date: 09/12/2022
Pot Size: $36,500 USDC
Total HM: 9
Participants: 69
Period: 3 days
Judge: Picodes
Total Solo HM: 2
Id: 190
League: ETH
Rank: 37/69
Findings: 2
Award: $80.96
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: obront
Also found by: 8olidity, HE1M, Madalad, Trust, cccz, csanuragjain, deliriusz, hansfriese, hihen, joestakey, rvierdiiev, wait, zaskoh
52.8446 USDC - $52.84
The minimum reserve can be set to zero (in fact it is zero default), putting fund at significant risk
Function ManagerWithdrawHook.sol#getMinReserve is used to limit how much reserve the manager can withdraw:
function getMinReserve() public view override returns (uint256) { return (depositRecord.getGlobalNetDepositAmount() * minReservePercentage) / PERCENT_DENOMINATOR; }
From the above code we can see that minReservePercentage
determines the minimum reserve percentage.
However, minReservePercentage
is left unset by default (nothing is done in constructor), meaning it defaults to 0.
Also, ManagerWithdrawHook.sol#setMinReservePercentage has no constraint on its minimum value, meaning it can be set to 0 manually.
This is a big risk for the reserves because manager can withdraw all the reserves at any time. i.e. all user funds can be drained easily.
Manual
uint256 public constant MIN_RESERVE_LOW_BOUND = 500000;
constructor() { minReservePercentage = MIN_RESERVE_LOW_BOUND; }
require(_newMinReservePercentage >= MIN_RESERVE_LOW_BOUND);
#0 - hansfriese
2022-12-14T17:42:23Z
duplicate of #254
#1 - c4-judge
2022-12-17T09:51:30Z
Picodes marked the issue as duplicate of #254
#2 - c4-judge
2023-01-01T17:40:55Z
Picodes marked the issue as satisfactory
🌟 Selected for report: 0xSmartContract
Also found by: 0Kage, 0x52, 0xAgro, 0xNazgul, 0xTraub, 0xhacksmithh, Awesome, Aymen0909, Bnke0x0, Englave, Janio, Mukund, Parth, RaymondFam, Rolezn, SmartSek, Tointer, UdarTeam, Udsen, Zarf, caventa, chaduke, csanuragjain, deliriusz, gz627, idkwhatimdoing, izhelyazkov, joestakey, neumo, obront, oyc_109, rvierdiiev, shark, trustindistrust, wait, yongskiws
28.124 USDC - $28.12
All deposited user assets may be drained.
Deposited user assets can be withdrawn by Collateral.sol#managerWithdraw:
function managerWithdraw(uint256 _amount) external override onlyRole(MANAGER_WITHDRAW_ROLE) nonReentrant { if (address(managerWithdrawHook) != address(0)) { managerWithdrawHook.hook(msg.sender, _amount, _amount); } baseToken.transfer(manager, _amount); }
All deposited assets can be drained easily and quickly if admin account(ADMIN_ROLE) is leaked or manager accounts(MANAGER_WITHDRAW_ROLE, SET_MANAGER_ROLE) are leaked.
All the hacker has to do is set the manager(to his EOA) and ManagerWithdrawHook (to 0), and then invoke managerWithdrawHook to transfer all base tokens stored in the contract to his account.
Manual
We should make manager and managerWithdrawHook immutable, their addresses are set to deployed open source contracts when the contract is created so that no one can change them.
#0 - Picodes
2022-12-13T20:58:17Z
This is a centralization issue, and a design choice, although it's right that it should be properly documented and disclosed
#1 - c4-judge
2022-12-13T20:58:32Z
Picodes changed the severity to QA (Quality Assurance)
#2 - c4-judge
2022-12-19T14:00:46Z
Picodes marked the issue as grade-b
#3 - c4-sponsor
2022-12-22T10:42:02Z
davidprepo marked the issue as sponsor disputed
#4 - ramenforbreakfast
2022-12-22T19:17:52Z
Disputing this issue because it is just pointing out 1 centralization issue that has already been mentioned by countless other more thorough submissions.