You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Improper Handling of the Zero Address in LidoVault.sol Withdrawal Arrays Will Cause Denial of Service for Vault Participants
Summary
Improper handling of the zero address during the finalization of withdrawal requests leads to a Denial of Service (DoS). Once a withdrawal request is finalized using the delete keyword, the user’s address is replaced with address(0). During subsequent finalization attempts, the contract encounters this address and reverts with the "WNR" (Withdrawal Not Ready) error, halting the withdrawal process and effectively preventing any further withdrawals or vault finalizations.
Root Cause
This issue arises from the delete keyword being used to remove user addresses from the fixedOngoingWithdrawalUsers and variableToVaultOngoingWithdrawalRequestIds arrays during withdrawal finalization. Instead of removing the entry, delete sets the entry to 0x0000000000000000000000000000000000000000. When the contract later encounters this invalid address in finalization processes, it reverts, stopping all future withdrawal requests from being processed.
The issue affects the following functions:
withdraw
finalizeVaultOngoingFixedWithdrawals
finalizeVaultOngoingVariableWithdrawals
In particular, the line of interest where the deletion occurs is: https://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L601 and https://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L695.
This vulnerability leads to a temporary Denial of Service, where users cannot successfully withdraw their funds due to the contract's reversion when encountering the zero address.
Internal pre-conditions
The contract must have users with ongoing withdrawals in fixedOngoingWithdrawalUsers or variableToVaultOngoingWithdrawalRequestIds.
Withdrawal requests must be initiated by calling withdraw function, leading to addresses being pushed to respective arrays.
External pre-conditions
External interaction or protocol change is not directly required for this issue to manifest, as it is contingent solely on the state management within the LidoVault contract itself.
Attack Path
User calls withdraw(SIDE.FIXED) to initiate a withdrawal.
User then calls finalizeVaultOngoingFixedWithdrawals, setting their address in the array to the zero address.
Any user attempting to finalize withdrawals thereafter encounters the zero address, causing the contract to revert with the WNR error, resulting in a denial of service.
Impact
The affected parties (users attempting to finalize withdrawals) cannot execute withdrawals or finalize them, causing funds to be locked without resolution unless the contract is updated or managed to bypass or handle zero address entries effectively.
PoC
Paste the following code in the withdraw section of tests within the 1.LidoVault.test.ts file.
asyncfunctionsetupWithdrawalFixture(){const{ lidoVault, addr1, addr2 }=awaitloadFixture(deployLidoVaultFixture)constdepositAmount=parseEther('200')awaitlidoVault.connect(addr1).deposit(SIDE.FIXED,{value: depositAmount})awaitlidoVault.connect(addr2).deposit(SIDE.FIXED,{value: depositAmount})return{ lidoVault, addr1, addr2 }}it('Should handle ZERO_ADDRESS in the withdrawal array without revert (Fixed DoS Test)',asyncfunction(){const{ lidoVault, addr1, addr2 }=awaitsetupWithdrawalFixture()awaitlidoVault.connect(addr1).withdraw(SIDE.FIXED)letfixedOngoingUsers=awaitlidoVault.fixedOngoingWithdrawalUsers(0)expect(fixedOngoingUsers).to.equal(addr1.address)awaitlidoVault.connect(addr1).finalizeVaultOngoingFixedWithdrawals()fixedOngoingUsers=awaitlidoVault.fixedOngoingWithdrawalUsers(0)expect(fixedOngoingUsers).to.equal(ZERO_ADDRESS)awaitlidoVault.connect(addr2).withdraw(SIDE.FIXED)fixedOngoingUsers=awaitlidoVault.fixedOngoingWithdrawalUsers(1)expect(fixedOngoingUsers).to.equal(addr2.address)awaitexpect(lidoVault.connect(addr1).finalizeVaultOngoingFixedWithdrawals()).to.not.be.revertedconstwithdrawalIds=awaitlidoVault.getFixedOngoingWithdrawalRequestIds(addr2.address)expect(withdrawalIds.length).to.equal(0)})it('Should handle ZERO_ADDRESS in the withdrawal array without revert (Variable DoS Test)',asyncfunction(){const{ lidoVault, addr1, addr2 }=awaitsetupWithdrawalFixture()awaitlidoVault.connect(addr1).withdraw(SIDE.VARIABLE)letvariableOngoingRequestIds=awaitlidoVault.variableToVaultOngoingWithdrawalRequestIds(addr1.address,0)expect(variableOngoingRequestIds).to.equal(1)awaitlidoVault.connect(addr1).finalizeVaultOngoingVariableWithdrawals()variableOngoingRequestIds=awaitlidoVault.variableToVaultOngoingWithdrawalRequestIds(addr1.address,0)expect(variableOngoingRequestIds).to.equal(ZERO_ADDRESS)awaitlidoVault.connect(addr2).withdraw(SIDE.VARIABLE)variableOngoingRequestIds=awaitlidoVault.variableToVaultOngoingWithdrawalRequestIds(addr2.address,0)expect(variableOngoingRequestIds).to.equal(1)awaitexpect(lidoVault.connect(addr1).finalizeVaultOngoingVariableWithdrawals()).to.not.be.revertedconstvariableWithdrawalIds=awaitlidoVault.getVariableToVaultOngoingWithdrawalRequestIds(addr2.address)expect(variableWithdrawalIds.length).to.equal(0)})it('Should revert when trying to finalize an ended vault withdrawal containing ZERO_ADDRESS (Fixed)',asyncfunction(){const{ lidoVault, addr1 }=awaitloadFixture(endVaultFixture)awaitlidoVault.connect(addr1).withdraw(SIDE.FIXED)awaitlidoVault.connect(addr1).finalizeVaultOngoingFixedWithdrawals()awaitexpect(lidoVault.finalizeVaultEndedWithdrawals(SIDE.FIXED)).to.be.revertedWith('WNR')})it('Should revert when trying to finalize an ended vault withdrawal containing ZERO_ADDRESS (Variable)',asyncfunction(){const{ lidoVault, addr1 }=awaitloadFixture(endVaultFixture)awaitlidoVault.connect(addr1).withdraw(SIDE.VARIABLE)awaitlidoVault.connect(addr1).finalizeVaultOngoingVariableWithdrawals()awaitexpect(lidoVault.finalizeVaultEndedWithdrawals(SIDE.VARIABLE)).to.be.revertedWith('WNR')})
Mitigation
Let claimFixedVaultOngoingWithdrawal return 0 if fixedUser is address(0) (see https://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L690-697).
The text was updated successfully, but these errors were encountered:
sherlock-admin4
changed the title
Jumpy Vanilla Gecko - Improper Handling of the Zero Address in LidoVault.sol Withdrawal Arrays Will Cause Denial of Service for Vault Participants
YowiSec - Improper Handling of the Zero Address in LidoVault.sol Withdrawal Arrays Will Cause Denial of Service for Vault Participants
Sep 30, 2024
YowiSec
Medium
Improper Handling of the Zero Address in LidoVault.sol Withdrawal Arrays Will Cause Denial of Service for Vault Participants
Summary
Improper handling of the zero address during the finalization of withdrawal requests leads to a Denial of Service (DoS). Once a withdrawal request is finalized using the delete keyword, the user’s address is replaced with
address(0)
. During subsequent finalization attempts, the contract encounters this address and reverts with the "WNR" (Withdrawal Not Ready) error, halting the withdrawal process and effectively preventing any further withdrawals or vault finalizations.Root Cause
This issue arises from the delete keyword being used to remove user addresses from the
fixedOngoingWithdrawalUsers
andvariableToVaultOngoingWithdrawalRequestIds
arrays during withdrawal finalization. Instead of removing the entry, delete sets the entry to0x0000000000000000000000000000000000000000
. When the contract later encounters this invalid address in finalization processes, it reverts, stopping all future withdrawal requests from being processed.The issue affects the following functions:
withdraw
finalizeVaultOngoingFixedWithdrawals
finalizeVaultOngoingVariableWithdrawals
In particular, the line of interest where the deletion occurs is:
https://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L601
andhttps://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L695
.This vulnerability leads to a temporary Denial of Service, where users cannot successfully withdraw their funds due to the contract's reversion when encountering the zero address.
Internal pre-conditions
The contract must have users with ongoing withdrawals in
fixedOngoingWithdrawalUsers
orvariableToVaultOngoingWithdrawalRequestIds
.Withdrawal requests must be initiated by calling withdraw function, leading to addresses being pushed to respective arrays.
External pre-conditions
External interaction or protocol change is not directly required for this issue to manifest, as it is contingent solely on the state management within the LidoVault contract itself.
Attack Path
withdraw(SIDE.FIXED)
to initiate a withdrawal.finalizeVaultOngoingFixedWithdrawals
, setting their address in the array to the zero address.WNR
error, resulting in a denial of service.Impact
The affected parties (users attempting to finalize withdrawals) cannot execute withdrawals or finalize them, causing funds to be locked without resolution unless the contract is updated or managed to bypass or handle zero address entries effectively.
PoC
Paste the following code in the withdraw section of tests within the 1.LidoVault.test.ts file.
Mitigation
Let
claimFixedVaultOngoingWithdrawal
return 0 if fixedUser isaddress(0)
(seehttps://github.com/sherlock-audit/2024-08-saffron-finance/blob/main/lido-fiv/contracts/LidoVault.sol?plain=1#L690-697
).The text was updated successfully, but these errors were encountered: