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: 36/69
Findings: 2
Award: $96.82
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: obront
Also found by: 8olidity, HE1M, Madalad, Trust, cccz, csanuragjain, deliriusz, hansfriese, hihen, joestakey, rvierdiiev, wait, zaskoh
68.698 USDC - $68.70
When a manager withdraws funds from Collateral.sol, there is a check in the managerWithdrawHook
to confirm that they aren't pushing the contract below the minimum reserve balance.
require(collateral.getReserve() - _amountAfterFee >= getMinReserve(), "reserve would fall below minimum");
However, a similar check doesn't happen in the withdraw()
function.
The manager can use this flaw to get around the reserve balance by making a large deposit, taking a manager withdrawal, and then withdrawing their deposit.
Imagine a situation where the token has a balance of 100, deposits of 1000, and a reserve percentage of 10%. In this situation, the manager should not be able to make any withdrawal.
But, with the following series of events, they can:
deposit()
with 100 additional tokensmanagerWithdraw()
to pull 100 tokens from the contractwithdraw()
to remove the 100 tokens they addedThe result is that they are able to drain the balance of the contract all the way to zero, avoiding the intended restrictions.
Manual Review
Include a check on the reserves in the withdraw()
function as well as managerWithdraw()
.
#0 - Picodes
2022-12-13T20:41:49Z
How would you implement your mitigation ? I don't see how it'd work considering that this would also your PoC would also hold using an other address to do the trick
#1 - Picodes
2022-12-17T09:41:55Z
From what I understand, although it's not clear from the documentation or the code, this minReserve
requirement is here to keep some funds in the contract to allow for withdrawals but does not provide any additional safety, and it should be clear for users that a compromised manager would immediately lead to a loss of all funds
#2 - Picodes
2022-12-17T09:47:01Z
I'll merge all issues regarding the manager being able to withdraw all funds, regardless of the method, the core issue being that the managerWithdrawHook check is easily bypassable.
#3 - c4-judge
2022-12-17T09:47:21Z
Picodes marked the issue as primary issue
#4 - c4-sponsor
2022-12-19T21:46:23Z
ramenforbreakfast marked the issue as sponsor disputed
#5 - c4-sponsor
2022-12-19T23:06:12Z
ramenforbreakfast marked the issue as sponsor confirmed
#6 - trust1995
2022-12-24T09:57:13Z
If this is where centralization issues are concentrated, I would suggest dropping the severity to Med.
#7 - Picodes
2023-01-01T17:33:31Z
Indeed, Med severity is more appropriate for this centralization issue. So far it was only the deduping phase.
#8 - c4-judge
2023-01-01T17:33:37Z
Picodes changed the severity to 2 (Med Risk)
#9 - c4-judge
2023-01-01T17:33:43Z
Picodes marked the issue as satisfactory
#10 - c4-judge
2023-01-01T17:37:13Z
Picodes marked the issue as selected for report
#11 - c4-judge
2023-01-01T17:37:19Z
Picodes marked the issue as not selected for report
#12 - c4-judge
2023-01-01T17:40:01Z
Picodes marked the issue as selected for report
#13 - Picodes
2023-01-01T17:40:26Z
Flagging as best for this centralization issue, combined with the other finding by the same warden https://github.com/code-423n4/2022-12-prepo-findings/issues/255
🌟 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
The manager
role in Collateral.sol must be set manually. It isn't included in the constructor or initialize functions.
It also isn't necessary in order to set the MANAGER_WITHDRAW_ROLE
.
In the case where the MANAGER_WITHDRAW_ROLE
is set and manager
is not, the user is able to call managerWithdraw()
.
This will send all requested funds to the zero address, where they will be irretrievable.
manager
isn't set anywhere in the contract except the setManager
function.
There is no requirement that this function must have been called in order to call managerWithdraw()
.
The result is that any call to managerWithdraw()
before manager
is set will destroy all funds.
Manual Review
Add a check in this function that the manager is set before sending funds:
require(manager != address(0), 'manager must be set to send funds');
#0 - Picodes
2022-12-13T20:38:54Z
This is theoretically possible but is not a high severity finding. It'd require centralization + errors. So downgrading to low as it's more a safety check.
#1 - c4-judge
2022-12-13T20:39:01Z
Picodes changed the severity to QA (Quality Assurance)
#2 - c4-judge
2022-12-19T13:53:24Z
Picodes marked the issue as grade-b
#3 - c4-sponsor
2022-12-22T10:43:02Z
davidprepo marked the issue as sponsor disputed
#4 - ghost
2022-12-22T10:43:12Z
System will never be left in this state