Platform: Code4rena
Start Date: 24/10/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 147
Period: 6 days
Judge: 0xDjango
Id: 299
League: ETH
Rank: 38/147
Findings: 1
Award: $161.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
161.7958 USDC - $161.80
A user with SOFT_RESTRICTED_STAKER_ROLE
role could call withdraw
to withdraw stUSDe for USDe without any restrictions. This is explicitly stated in the documentation as not being allowed.
According to the README, SOFT_RESTRICTED_STAKER_ROLE
cannot deposit or withdraw.
Due to legal requirements, there's a SOFT_RESTRICTED_STAKER_ROLE and FULL_RESTRICTED_STAKER_ROLE. The former is for addresses based in countries we are not allowed to provide yield to, for example USA. Addresses under this category will be soft restricted. They cannot deposit USDe to get stUSDe or withdraw stUSDe for USDe. However they can participate in earning yield by buying and selling stUSDe on the open market.
However, the _withdraw
doesn't check that owner
is not a SOFT_RESTRICTED_STAKER_ROLE
. A SOFT_RESTRICTED_STAKER_ROLE
user could still call this function to withdraw stUSDe.
function _withdraw(address caller, address receiver, address _owner, uint256 assets, uint256 shares) internal override nonReentrant notZero(assets) notZero(shares) { //@audit: owner should not be SOFT_RESTRICTED_STAKER_ROLE if (hasRole(FULL_RESTRICTED_STAKER_ROLE, caller) || hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver)) { revert OperationNotAllowed(); } super._withdraw(caller, receiver, _owner, assets, shares); _checkMinShares(); }
Considering that alice has deposited some USDe and got some stUSDe, but then she is added to a black list with the role SOFT_RESTRICTED_STAKER_ROLE
. However, she could still withdraw her stUSDe.
Add the below test to test/foundry/staking/StakedUSDe.t.sol
and run it with forge test --match-test testSoftWithdraw -vv
function testSoftWithdraw() public { bytes32 BLACKLIST_MANAGER_ROLE = keccak256("BLACKLIST_MANAGER_ROLE"); vm.prank(owner); stakedUSDe.grantRole(BLACKLIST_MANAGER_ROLE, alice); uint256 amount = 2 ether; usdeToken.mint(alice, amount); vm.startPrank(alice); usdeToken.approve(address(stakedUSDe), amount); stakedUSDe.deposit(amount, alice); // add alice to blacklist stakedUSDe.addToBlacklist(alice, false); // alice can still withdraw stakedUSDe.withdraw(amount, alice, alice); }
Foundry
Check the role of owner
in _withdraw
.
@@ -229,7 +231,7 @@ contract StakedUSDe is SingleAdminAccessControl, ReentrancyGuard, ERC20Permit, E notZero(assets) notZero(shares) { - if (hasRole(FULL_RESTRICTED_STAKER_ROLE, caller) || hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver) || hasRole(SOFT_RESTRICTED_STAKER_ROLE, _owner)) { + if (hasRole(FULL_RESTRICTED_STAKER_ROLE, caller) || hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver)) { revert OperationNotAllowed(); }
Access Control
#0 - c4-pre-sort
2023-10-31T00:48:56Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-31T00:49:12Z
raymondfam marked the issue as duplicate of #52
#2 - c4-judge
2023-11-10T21:38:31Z
fatherGoose1 marked the issue as satisfactory
#3 - c4-judge
2023-11-14T17:21:24Z
fatherGoose1 changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-11-27T21:34:38Z
fatherGoose1 marked the issue as selected for report
#5 - c4-judge
2023-11-27T21:58:28Z
fatherGoose1 marked issue #246 as primary and marked this issue as a duplicate of 246