-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A snapshot may face a permanent DoS if both a slashing event occurs in the NativeVault and the staker's validator is penalized. #31
Comments
MiloTruck marked the issue as primary issue |
A validator getting slashed for its entire effective balance is only possible due to the correlation penalty, which is extremely unlikely. Historically, most slashing penalties have been around 1 ETH. The following conditions must be met for this issue to occur:
I believe this is extremely unlikely to occur, and as such, medium severity is appropriate. |
MiloTruck marked the issue as unsatisfactory: |
MiloTruck removed the grade |
MiloTruck changed the severity to 2 (Med Risk) |
MiloTruck marked the issue as satisfactory |
MiloTruck marked the issue as selected for report |
@MiloTruck I do not consider this valid for few reasons;
It is not reasonable to lose 32ETH on purpose to get rewards that would definitely be lower. Also, it is clearly stated that, DSS is responsible for reward distribution and it is not just based on shares check comment on #58. It is the DSS responsibility to tell if validator of node is ACTUALLY staked in beacon
LIKELIHOOD: extremely low At best, this should count as QA. |
Agree with @lanrebayode PLUS, the fact that the user is not gonna be able to withdraw his rewards in anyway, makes this issue totally invalid because when the snapshot gets expired, users cannot call function startWithdrawal(address to, uint256 weiAmount)
external
nonReentrant
nodeExists(msg.sender)
whenFunctionNotPaused(Constants.PAUSE_NATIVEVAULT_START_WITHDRAWAL)
returns (bytes32 withdrawalKey)
{
// TODO: make more recent snapshot compulsory
NativeVaultLib.Storage storage self = _state();
NativeVaultLib.NativeNode storage node = self.ownerToNode[msg.sender];
if (weiAmount > withdrawableWei(msg.sender) - self.nodeOwnerToWithdrawAmount[msg.sender]) {
revert WithdrawMoreThanMax();
}
self.nodeOwnerToWithdrawAmount[msg.sender] += weiAmount;
@> if (block.timestamp >= node.lastSnapshotTimestamp + Constants.SNAPSHOT_EXPIRY) {
revert SnapshotExpired();
}
withdrawalKey = NativeVaultLib.calculateWithdrawKey(msg.sender, self.nodeOwnerToWithdrawNonce[msg.sender]++);
address nodeOwner = msg.sender;
self.withdrawalMap[withdrawalKey].to = to;
self.withdrawalMap[withdrawalKey].assets = weiAmount;
self.withdrawalMap[withdrawalKey].nodeOwner = nodeOwner;
self.withdrawalMap[withdrawalKey].start = uint96(block.timestamp);
emit StartedWithdraw(nodeOwner, _config().operator, withdrawalKey, weiAmount, to);
return withdrawalKey;
} |
@lanrebayode
This statement is completely incorrect, as the account snapshot for the user is not available, which is the core of this issue and is explained in the report. @ShaheenRehman
This statement is incorrect. First, even though the user cannot currently withdraw their reward, granting rewards to one user results in a loss of funds for other users. Second, the rewards will become withdrawable once a sufficient amount has been accumulated. |
Doesn't make any sense to me tbh. Rewards Distribution is handled by the DSS, If DSS notices anything malicious he will just jail the operator/validator. Rewards are not automatic. Anyway, this was a good find ngl, but it have zero impact, infact its a total loss for the operator itself, losing 32 ETH to earn rewards which can be withdrawalable after a few months/years, absolutely makes no sense. |
@ShaheenRehman You mentioned that:
This is inaccurate because, while the DSS can jail an operator, it cannot target a specific staker. As a result, this could lead to the loss of funds for other stakers within the same nativeVault. You also mentioned that:
Losing 32 ETH is a possibility, but it is not the intention of the staker. The risk of losing ETH significantly increases when the staker is a malicious validator. They can, however, mitigate their losses from such malicious actions by staking into the nativeVault. |
According to the C4 severity categorization, the criteria for medium severity is:
As stated in my initial comment, this requires both Karak and beacon chain slashing to occur. The upper limit of how much the DSS can slash an account for is 100%: uint256 public constant HUNDRED_PERCENT_WAD = 100e18;
uint256 public constant MAX_SLASHING_PERCENT_WAD = HUNDRED_PERCENT_WAD; Therefore, it is possible for the sum of Karak and beacon chain slashing to exceed 100%, however unlikely. I view this as "with stated assumptions, but external requirements". Regarding impact, As such, this remains as medium. |
Lines of code
https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L126-L162
https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L476-L495
https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L517-L525
https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L507-L515
Vulnerability details
Impact
The staker can still receive rewards even if their validators are empty.
Proof of Concept
Let's consider the following scenario:
Alice has a validator whose balance is 32 ETH.
A slashing event occurs in the NativeVault, resulting in Alice being slashed by 2 ETH, reducing her withdrawable assets to 30 ETH.
Subsequently, Alice's validator loses all its funds (this can happen within the 7-day snapshot period, even within just 1 hour). A snapshot should then be taken to reduce Alice's assets by 32 ETH, which requires calling the
validateSnapshotProofs()
function for Alice's validator.In this call,
snapshot.balanceDeltaWei
is set to -32 ETH, and the_updateSnapshot()
function is invoked atL161
.In the
_updateSnapshot()
function, the_updateBalance()
function is invoked atL490
, withtotalDeltaWei = -32 ETH
, as noted atL482
.Within the
_updateBalance()
function, the_decreaseBalance()
function is called atL521
with a decrease amount of 32 ETH.In the
_decreaseBalance()
function, shares are burned atL511
.The problem arises in step 6. Here, the shares being burned correspond to 32 ETH, while Alice's shares only amount to 30 ETH. This discrepancy causes the burn amount to exceed the existing share amount, leading to a reversal of the transaction. As a result, the snapshot for Alice will be permanently
DoS
ed, and her share amount will remain unchanged. Consequently, Alice can still receive rewards even though her validator is empty. In addition, nobody can callvalidateExpiredSnapshot()
for Alice.A malicious staker could intentionally orchestrate this scenario by creating a validator and engaging in malicious behavior. If the malicious action fails, they would lose all 32 ETH but could still receive rewards.
Tools Used
Manual review
Recommended Mitigation Steps
The
_decreaseBalance()
function should be enhanced to properly handle cases where the burning amount exceeds the existing share amount.For example:
Assessed type
DoS
The text was updated successfully, but these errors were encountered: