Open c4-submissions opened 1 year ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as duplicate of #7
raymondfam marked the issue as duplicate of #666
fatherGoose1 marked the issue as satisfactory
fatherGoose1 changed the severity to 2 (Med Risk)
Hi @fatherGoose1, this is not a duplicate of #666. #666 is a vulnerability related to the FULL_RESTRICTED_STAKER_ROLE
depositor, whereas this report addresses a vulnerability regarding the FULL_RESTRICTED_STAKER_ROLE
receiver.
There are two StakedUSDe contracts, StakedUSDe
and StakedUSDeV2
, where StakedUSDeV2
inherits from StakedUSDe
.
When cooldownDuration=0, or StakedUSDe is used, users can call withdraw
or redeem
for withdrawals. In this case, if the owner is not a FULL_RESTRICTED_STAKER_ROLE
, but the receiver is a FULL_RESTRICTED_STAKER_ROLE
, the withdrawal will not succeed.
When cooldownDuration!=0, users can call cooldownAssets
and cooldownShares
for withdrawals. In this scenario, the withdrawal will be successful.
The issue arises because when users call cooldownAssets
or cooldownShares
, StakedUSDeV2 invokes the _withdraw
function, and the recipient's address at this point is the silo, which is not a FULL_RESTRICTED_STAKER_ROLE
. When users call the unstake
function to withdraw from the silo, StakedUSDeV2 does not check the address of the receiver.
Please review my report and PoC one more time. Thanks.
fatherGoose1 marked the issue as not a duplicate
fatherGoose1 marked the issue as unsatisfactory: Invalid
fatherGoose1 changed the severity to QA (Quality Assurance)
fatherGoose1 marked the issue as grade-b
This report shares impact with #430. Per the sponsor in that issue:
It's a good spot however I don't think it warrants a code change - if anything unstake is a bit of a misnomer - if a user has called cooldownAssets or cooldownShares their sUSDe has already been settled for USDe - they are no longer staked - they just need to wait to receive their USDe. Transfers of USDe are fully permissionless by design - note that there is no blacklisting in the USDe.sol. Therefore we are ok with the now blacklisted user being able to get the cooled down USDe
In this report, the blacklisted receiver is able to receive USDe since USDe is not permissioned. Leaving as QA.
Lines of code
https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L78-L90
Vulnerability details
Impact
According to Ethena's design, due to legal requirements, there's a FULL_RESTRCITED_STAKER_ROLE, which is for sanction/stolen funds, or if Ethena gets a request from law enforcement to freeze funds. Addresses fully restricted cannot transfer, stake, or unstake.
In the
StakedUSDeV2
contract, there is a settingcooldownDuration
. When the value ofcooldownDuration
is 0, the withdrawal process follows the same procedure as in theStakedUSDe
contract. Where the_withdraw
function checks thereceiver
and requires that thereceiver
does not have theFULL_RESTRICTED_STAKER_ROLE
role. https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDe.sol#L225-L238When the value of
cooldownDuration
is not 0, the withdrawal process involves two steps: (1) Users call thecooldownShares
orcooldownAssets
functions to withdraw USDe to the silo. (2) Users can then claim the USDe by calling theunstake
function after the cooldown period has finished.When users execute the
unstake
function, they can specify the address to receive USDe tokens.function unstake(address receiver) external
. However, there is no check here to verify whether thereceiver
has the FULL_RESTRICTED_STAKER_ROLE role. Therefore, when the value of cooldownDuration is not 0, it is possible to unstake to a user who has the FULL_RESTRICTED_STAKER_ROLE role. https://github.com/code-423n4/2023-10-ethena/blob/ee67d9b542642c9757a6b826c82d0cae60256509/contracts/StakedUSDeV2.sol#L78-L90Proof of Concept
Below is a test scenario:
user1
is a regular user, andreceiver
is a user with the FULL_RESTRICTED_STAKER_ROLE role.user1
successfully unstakes and transfers funds toreceiver
by executing thedeposit
andunstake
functions.Tools Used
Foundry
Recommended Mitigation Steps
In the
unstake
function, check whetherreceiver
has the FULL_RESTRICTED_STAKER_ROLE role. Ifreceiver
has the FULL_RESTRICTED_STAKER_ROLE role, reject the withdrawal.if (hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver)) revert InvalidAddress()
Assessed type
Context