Skip to content

NodeOperator will steal other NodeOperators' validators through frontrunning #348

@code423n4

Description

@code423n4

Lines of code

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessNodeRegistry.sol#L125-L172

Vulnerability details

Impact

People that want to earn staking rewards, but do not have the resources to run validators, will see Stader as an avenue where they can easily steal and use others validators and they will actually earn Operator rewards from Stader.
This could lead to two things

  • Original validator owner would continue performing his duties, but he would be laboring for another person. And if he decides to withdraw, the validator thief would lose nothing
  • Original validator owner would want to behave maliciously to punish the validator thief. But, this would affect Stader because when the validator gets slashed below 28 eth, stakers' funds would be at risk

Proof of Concept

If a NodeOperator calls addValidatorKeys to use validators that he operates, or that he trusts, another NodeOperator that sees the pending transaction in the mempool, could frontrun the original NodeOperator by calling addValidatorKeys with the same parameters, and the validator would be registered under him, while the original NodeOperator's transaction would get reverted.

function addValidatorKeys(
        bytes[] calldata _pubkey,
        bytes[] calldata _preDepositSignature,
        bytes[] calldata _depositSignature
    ) external payable override nonReentrant whenNotPaused {
    ...
    IPermissionlessPool(staderConfig.getPermissionlessPool()).preDepositOnBeaconChain{
            value: staderConfig.getPreDepositSize() * keyCount
        }(_pubkey, _preDepositSignature, operatorId, operatorTotalKeys);
}

Since the addValidatorKeys function does not perform any sanity checks on the caller, and the eth2.0 deposit contract's caller is Stader contracts, there is no way to know the true owner of a pubkey.

ATTACK SCENARIO

  • Node Operator Alice with _pubkeyA signs _preDepositSignatureA and _depositSignatureA
  • Alice calls addValidatorKeys(_pubkeyA,_preDepositSignatureA, _depositSignatureA)
  • Node Operator Bob sees the transaction in mempool, and calls addValidatorKeys(_pubkeyA,_preDepositSignatureA, _depositSignatureA) with higher gas
  • Bob's transaction gets executed before Alice's so even though Alice is the true owner of _pubkeyA, it get registered for Bob
  • Since the signatures are valid, it would be considered valid in eth2.0
  • If Alice's Validator continues performing its duties, or decides to exit, Bob gains his eth back plus rewards
  • If Alice decides to perform maliciously to punish Bob, and gets slashed to 16 eth on Beacon Chain, Bob loses his 4 eth while Stader loses 12 eth.

Tools Used

Manual Review

Mitigation Steps

Consider creating a library for BLS12-381 curves using EIP 2537. With that, you may require the Node Operator to sign a confirmation signature using the private key he used to sign the _preDepositSignature. If the pubkey extracted from the confirmation signature is equal to the pubkey he provided, then he is the true owner of the validator and should be activated.

Assessed type

Access Control

Metadata

Metadata

Assignees

No one assigned

    Labels

    2 (Med Risk)Assets not at direct risk, but function/availability of the protocol could be impacted or leak valuebugSomething isn't workingedited-by-wardensponsor disputedSponsor cannot duplicate the issue, or otherwise disagrees this is an issueunsatisfactorydoes not satisfy C4 submission criteria; not eligible for awards

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions