Open sherlock-admin2 opened 11 months ago
1 comment(s) were left on this issue during the judging contest.
polarzero commented:
Medium. Perfectly explained and demonstrated in the report.
Vault(user,0,0,0)
increasing checkpoint.count
(medium severity) - fixedcheckpoint.count
, although he pays for the claim fully and doesn't participate in paying the keeper fee for deposit/redeem, thus he shouldn't increase checkpoint.count
(low severity) - not fixedSince (2) is overcharging the fee instead of undercharging (claim pays entire settlement fee instead of splitting the resulting fee with others in version), we're going to leave this as-is. We'll make a note of this in case we improve the claiming flow in the future.
Based on @kbrizzle's comment, Sherlock will consider the issue brought up in #2 in the comment above by panprog as acknowledged.
panprog
medium
Vault.update(anyUser,0,0,0)
can be called for free to increasecheckpoint.count
and pay smaller keeper fee than necessarySummary
Vault re-balances its deposits and redemptions once per oracle epoch, but since multiple users can deposit/redeem, rebalance keeper fee is shared equally between all users depositing and redeeming in the same epoch. For this reason,
checkpoint.count
counts the number of users who will split the keeper fee (each user payskeeper fee / checkpoint.count
)The problem is that there are currently 2 types of users who increase
checkpoint.count
while they do not pay any fee:Vault.update(user, 0, 0, 0)
isn't charged any fee but increasescheckpoint.count
settlementFee
from the amount he claims, but is not charged any other (shared) fees after that, but still increasescheckpoint.count
The 1st point is more severe, because it allows to intentionally reduce the keeper fee paid by calling
Vault.update(0,0,0)
from different random accounts (which costs only gas fees and nothing more). Each such call increasescheckpoint.count
and thus reduces the keeper fees paid by the attacker.Attack scenario:
checkpoint.count = 1
for the epoch. Normally the user will pay the sum of thesettlementFee
of all the vault markets.Vault.update(address1/2/3, 0,0,0)
. Each of these calls costs only gas to the user, but increasescheckpoint.count
to the value of 4.settlementFee / 4
for his deposit/withdrawal, but the vault will still pay the Markets fullsettlementFee
at the expense of the other vault users.Vulnerability Detail
Vault._update(user, 0, 0, 0)
will pass all invariants checks:It then calculates amount to claim by calling
_socialize
:_socialize
will immediately return 0 ifcontext.global.assets == 0
. Ifcontext.global.assets > 0
, then this function will revert in the last line due to underflow (trying to subtractsettlementFee
from 0claimAmount
)This is the condition for this issue to happen: global assets must be 0. Global assets are the amounts redeemed but not yet claimed by users. So this can reasonably happen in the first days of the vault life, when users mostly only deposit, or claim everything they withdraw.
Once this function passes, the following lines increase
checkpoint.count
:The rest of the function executes normally.
During position settlement, pending user deposits and redeems are reduced by the
keeper fees / checkpoint.count
:Also notice that in
processLocal
the only thing which keeper fees influence are deposits and redemptions, but not claims.Proof of concept
The scenario above is demonstrated in the test, add this to Vault.test.ts:
Console output:
So the user2 inflates his deposited amounts by paying smaller keeper fee.
If 2 lines which inflate checkpoint count (after corresponding comment) are deleted, then the output is:
So if not inflated, user2 pays correct amount and has roughly the same assets as user1 after his deposit.
Impact
Malicious vault user can inflate
checkpoint.count
to pay much smaller keeper fee than they should at the expense of the other vault users.Code Snippet
Vault.update(0,0,0)
will increasecheckpoint.count
here: https://github.com/sherlock-audit/2023-09-perennial/blob/main/perennial-v2/packages/perennial-vault/contracts/Vault.sol#L300Tool used
Manual Review
Recommendation
Consider reverting (0,0,0) vault updates, or maybe redirecting to
settle
in this case. Additionally, consider updating checkpoint only ifdepositAssets
orredeemShares
are not zero: