Open code423n4 opened 1 year ago
0xSorryNotSorry marked the issue as primary issue
The design is intended and the suggested changes would be unsafe.
Sidu28 marked the issue as sponsor disputed
GalloDaSballo changed the severity to QA (Quality Assurance)
Downgrading to QA Refactoring, but agree that this is not a vulnerability
Lines of code
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/pods/EigenPod.sol#L157-L163 https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/pods/EigenPod.sol#L217-L220 https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/pods/EigenPod.sol#L453-L457
Vulnerability details
Impact
In EigenPod.sol, the state variable,
withdrawBeforeRestaking()
serves good only for the first ETH validator. As soon as its default value is settrue
inverifyWithdrawalCredentialsAndBalance()
,withdrawBeforeRestaking()
will be uncallable for the next ETH validator that has not been restaked.Proof of Concept
Before
eigenPodManager.restakeBeaconChainETH()
is invoked inverifyWithdrawalCredentialsAndBalance()
,hasRestaked == false
is turned intohasRestaked == true
.File: EigenPod.sol#L217-L225
This in turn makes the modifier
hasNeverRestaked
readily revert from now on.File: EigenPod.sol#L114-L117
Consequently,
withdrawBeforeRestaking()
associated withhasNeverRestaked
visibility will not be callable by the pod owner for all subsequent ETH validators created by the owner even though they have not been restaked yet. The only option to retrieve the rewards is via the restake path, i.e. firstverifyWithdrawalCredentialsAndBalance()
thenverifyAndProcessWithdrawal()
.File: EigenPod.sol#L453-L457
Tools Used
Manual
Recommended Mitigation Steps
A better approach would be to maintain a separate
hasRestaked
mapping for each validator instead of using a single boolean for the entire contract. This way, you can track the restaking status for each validator individually.Here's how you can modify the contract:
Replace the current bool public hasRestaked; declaration with a mapping. Additionally, introduce another mapping to track the balance of each validator:
Modify
verifyWithdrawalCredentialsAndBalance()
to set thehasRestaked
flag for the specific validator:Modify the
hasNeverRestaked
modifier to use the mapping instead of the single boolean:Update
withdrawBeforeRestaking()
to take avalidatorIndex
parameter and use thehasNeverRestaked modifier
with the provided index:With these changes, the contract will maintain separate restaking statuses for each validator, allowing
withdrawBeforeRestaking()
to be called for each validator individually before they have been restaked.Assessed type
Context