Closed code423n4 closed 1 year ago
after first valid deposit on beacon chain, it does not verify signature of other deposit, it just increases the validator balance
manoj9april marked the issue as sponsor disputed
Picodes changed the severity to 2 (Med Risk)
@manoj9april I am not sure to understand your remark. Although there are economic safeguards to prevent this, what prevents an invalid _depositSignature
from being used and ultimately lead to a costly griefing attack?
Picodes marked the issue as satisfactory
As I understand, since an oracle will verify that the pre-deposit signature is a valid BLS signature, I don't see why it couldn't also verify the deposit sig, before marking it as ready to deposit, but I'm not that deep into ETH2.0, so I might be wrong.
So from what I understand from what the sponsor sent me (thanks!) and what I've read, a validator's signature is checked only once as it is only a mitigation against rogue public keys attacks. Therefore, as in our case the validators already had a pre-deposit, an invalid signature isn't an issue.
Picodes marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessNodeRegistry.sol#L128 https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedNodeRegistry.sol#L143
Vulnerability details
Impact
Node Operators can
addValidatorKeys
with a valid_pubkey
, a valid_preDepositSignature
but an invalid_depositSignature
, leading to loss of users' funds. This attack may/may not be intentional. Here is a good reason why someone would gladly want to do this. Although the Node Operator would lose his 4 eth bond + (0.4 -> 2) eth worth of SD, 28 eth of users' stake will also be lost, so the protocol would be affected far more than the malicious Node Operator. Considering the amount of users' funds that could potentially be lost by a single careless or rich malicious Node Operator, and the possibility of even draining the StaderStakePoolsManager contract, I believe this is of HIGH impact.Proof of Concept
FUNCTIONALITY/WORKFLOW
addValidatorKeys
which assigns to the validator thenextValidatorId
and then make a predeposit of 1 eth using the_preDepositSignature
.markValidatorReadyForDeposit
:_pubkey
passes these checks, operator adds the validator to a queue for its full deposit of 31 eth to be processed by callingmarkValidatorReadyForDeposit
.validatorBatchDeposit
ordepositETHOverTargetWeight
to loop through the validator queue and deposit 31 eth for each validator using their_depositSignature
.THE PROBLEM
The
_depositSignature
provided should have been signed with a private key. This private key would later be required to sign the withdrawal transaction. Info from this site.The PROBLEM is that if a Node Operator
addValidatorKeys
with an invalid_depositSignature
(a signature that was not signed with pubkey's private key), he would be successfully added to the validator queue, and whenvalidatorBatchDeposit
is called, 28 eth of users stake + 3 eth of Node Operator's bond would be deposited using the_depositSignature
he provided. This 31 eth would be burned on eth1, but considered "invalid" on ETH2 due to the invalid signature provided.Going through the code for the eth2.0 deposit contract, the actual signature is not verified there in the contract(only the length is checked) but rather on the Beacon Chain nodes. The reason for this is that EVM did not support BLS curve operations.
This would make
StaderStakePoolManager#validatorBatchDeposit
orStaderStakePoolManager#depositETHOverTargetWeight
to successfully execute, but the funds will never be withdrawable.Although Operators check that predeposit signature is valid by querying an API before calling
markValidatorReadyForDeposit
, there is no check to know if the_depositSignature
is valid.Here is what happened to eth of a person that attempted to deposit using an invalid signature
ATTACK SCENARIO
_preDepositSignature
using his validator private keyaddValidatorKeys
with his validatorpubkey
,_preDepositSignature
he signed, and a_depositSignature
which is a random signature with a length of 96 bytes.validatorBatchDeposit
ordepositETHOverTargetWeight
, and Node Operator's full deposit of 31 eth is executed by calling ETH2.0DepositContract#deposit(pubkey,withdrawal_credentials,_depositSignature
) which will not revert because no signature validation is done.Tools Used
Manual Review
Recommended Mitigation Steps
EIP-2537 will offer the main curve operations that will enable the protocol to verify the actual signature and reject any attempts to use an invalid validator signature right on chain. This will enable innovations like decentralized staking pools based on distributed-key-generation. The protocol may have to work towards making EIP-2537 BLS curve libraries, or wait till one is created before this innovation would be feasible and risk free. More info in this blog post. When that is accomplished, both
_preDepositSignature
and_depositSignature
should be verified and validated on chain before deposit calls to eth2.0 deposit contract are made.Assessed type
Other