Open c4-submissions opened 1 year ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as duplicate of #4
raymondfam marked the issue as not a duplicate
raymondfam marked the issue as primary issue
raymondfam marked the issue as high quality report
FJ-Riveros (sponsor) confirmed
This griefing attack relies on a previously-approved address to act maliciously. I find this point to be invalid. The approved address is trusted by the approver.
I agree that the current cooldown mechanism is inflexible, but does not warrant a medium severity.
fatherGoose1 marked the issue as unsatisfactory: Invalid
This griefing attack relies on a previously-approved address to act maliciously. I find this point to be invalid. The approved address is trusted by the approver.
I agree that the current cooldown mechanism is inflexible, but does not warrant a medium severity.
I already wrote a comment on my issue, but I will do so again since this is the main one. Providing allowances is not necessarily a fully trusted action and there are multiple scenarios where one could need to provide allowance to a 3rd party. Providing rights over some amount of tokens, be it leftovers from the allowance, should NOT allow approved parties to tamper with the main users vesting periods throughout protocols. I believe this to be a combination of a broken trust assumption + a loophole in the code. Even if the judge does not agree that the code has a fault for the existence of the issue, I still believe these are QA at worst since it is a possible scenario of griefing that the protocol essentially allows.
This griefing attack relies on a previously-approved address to act maliciously. I find this point to be invalid. The approved address is trusted by the approver.
Hi @fatherGoose1, thanks for judging.
I will like to say that the approved address bricking the withdrawal of the user may not always be a malicious action. In most cases which i believe it will be, it will be a honest action. Just approved users trying to do what they ought to do, take actions the protocol allows them to and thereby causing undesired effects on the other user.
I will like to add that it will not be a very great development for the sponsors to overlook this issue based on the high likelihood of it happening many times when the code is live.
This griefing attack relies on a previously-approved address to act maliciously. I find this point to be invalid. The approved address is trusted by the approver. I agree that the current cooldown mechanism is inflexible, but does not warrant a medium severity.
I already wrote a comment on my issue, but I will do so again since this is the main one. Providing allowances is not necessarily a fully trusted action and there are multiple scenarios where one could need to provide allowance to a 3rd party. Providing rights over some amount of tokens, be it leftovers from the allowance, should NOT allow approved parties to tamper with the main users vesting periods throughout protocols. I believe this to be a combination of a broken trust assumption + a loophole in the code. Even if the judge does not agree that the code has a fault for the existence of the issue, I still believe these are QA at worst since it is a possible scenario of griefing that the protocol essentially allows.
I agree that this should be treated as QA. The current design is inflexible and can allow for intentional or accidental tampering with user cooldowns. That said, I do not believe this to warrant medium severity as a user needs to have set the approval in the first place, placing significant trust in the approvee.
QA is fine imo
fatherGoose1 changed the severity to QA (Quality Assurance)
fatherGoose1 marked the issue as grade-b
Lines of code
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L95 https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L111
Vulnerability details
Impact
StakedUSDeV2
is erc4262 and as thus it can allow for owners of shares to approve other addresses to spend/withdraw on their behalf. An approved address or owner address can can call the functions cooldownShares() and cooldownAssets() on behalf of the owner to begin the cooldown process.In a case whereby alice is owner of 100 tokens. alice approves 40 tokens for bob to spend. Alice then calls
cooldownShares()
orcooldownAssets()
to cooldown 60 of her tokens,cooldowns[owner].coolDownEnd
is set in storage to 90 DAYS and alice is left with 40 tokens. In 89 days, Bob realizes he's got approval from Alice and he callscooldownShares()
orcooldownAssets()
on behalf of alice to cool down 40 token shares. Bob will reset thecooldowns[owner].coolDownEnd
value to be +90 days on the 89th day. Alice callsunstake()
on the 90th day but alice will fail and alice will not be able to to collect the 60 tokens she had cooled down her self 90 days ago. She will have to wait for 89 more days because of the action of Bol on the 89th day. This means alice had her cooldown period for her withdrawal of 60 tokens exetended for more than the MAX_COOLDOWN_DURATION (90 days).Withdrawal actions by alice should not be affected by bob since the actions were done at different times.
Proof of Concept
in the snippet below, the functions
cooldownShares()
orcooldownAssets()
call_withdraw
which is an erc4262 function that allows for the caller to be the owner or for the caller to have been approved to spend an amount of tokens by the owner.alice approves Bob for 40 tokens.
Bob doesnt go ahead to call
cooldownShares()
orcooldownAssets()
immediately,Alice tries to coolDown 60 tokens from her balance and calls
cooldownShares()
orcooldownAssets()
. Call is sucessful andcooldowns[owner].cooldownEnd
is updated to be 90 days from now. Note that by default,cooldownDuration
is set to be 90 days in constructor. so cooldownDuration is 90 days except changed.in 89 days Bob decides to call
cooldownShares()
orcooldownAssets()
to spend the 40 tokens Alice gave him approval for. Call tocooldownShares()
orcooldownAssets()
is sucessful andcooldowns[owner].cooldownEnd
is updated again to be current timestamp + 90 days.time gets to the 90th day and Alice wants to take her cooled down assets since she was told it will take 90 days to cool down. Alice calls unstake() but cant withdraw from silo to her address because Bob's action on the 89th day has affected
cooldowns[owner].cooldownEnd
value she set when she decided to coolDown 60 tokens. Now alice funds are unfairly stuck for 89 more days because thecooldowns[owner]
mapping tracks values per owner address only and not by owner address and per withdrawal action. Unstake() reverts since block.timestamp is not greater than or equal thecooldowns[owner].cooldownEnd
here is a coded POC illustrating this issue :
run with
forge test --mt test_UnstakeUnallowed
Tools Used
manual review, foundry
Recommended Mitigation Steps
change
cooldowns
to become a double mapping of owner and id of withdrawal action. This way every withdrawal action will be unique and will have different coolDownEnd times. And actions by alice and bob wont have the same coolDown times since the actions were done at different times.Assessed type
Error