Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Dependence on External Merkle Proofs

Summary

The PriorityPool.sol contract within the stake.link platform relies heavily on externally generated Merkle proofs for critical operations such as unqueuing and withdrawing tokens. This dependency introduces a vulnerability where the integrity and availability of user funds are contingent upon the correct and timely updating of the merkleRoot by the centralized distributionOracle. If the oracle fails or is compromised, users may be unable to access their funds, leading to significant operational and financial repercussions.

Vulnerability Details

1. Centralized Control of Merkle Root

address public distributionOracle;
bytes32 public merkleRoot;
modifier onlyDistributionOracle() {
if (msg.sender != distributionOracle) revert SenderNotAuthorized();
_;
}
function updateDistribution(
bytes32 _merkleRoot,
bytes32 _ipfsHash,
uint256 _amountDistributed,
uint256 _sharesAmountDistributed
) external onlyDistributionOracle {
merkleRoot = _merkleRoot;
// Additional update logic...
}

Explanation:
The merkleRoot is exclusively updated by the distributionOracle through the updateDistribution function. This centralized control means that the integrity of the Merkle proofs, which are essential for users to unqueue and withdraw their tokens, is entirely dependent on the distributionOracle's reliability and security.

2. Lack of Redundancy in Merkle Root Updates

function setDistributionOracle(address _distributionOracle) external onlyOwner {
distributionOracle = _distributionOracle;
}

Explanation:
The contract allows only the contract owner to set the distributionOracle. There is no mechanism to have multiple oracles or community governance involved in updating the merkleRoot. This single point of control increases the risk of failure or malicious manipulation, as there are no alternative pathways for updating critical state variables.

3. Dependency in Withdrawal Operations

function withdraw(
uint256 _amountToWithdraw,
uint256 _amount,
uint256 _sharesAmount,
bytes32[] calldata _merkleProof,
bool _shouldUnqueue,
bool _shouldQueueWithdrawal
) external {
// Merkle proof verification
bytes32 node = keccak256(
bytes.concat(keccak256(abi.encode(account, _amount, _sharesAmount)))
);
if (!MerkleProofUpgradeable.verify(_merkleProof, merkleRoot, node))
revert InvalidProof();
// Withdrawal logic...
}

Explanation:
Withdrawal operations rely on the validity of Merkle proofs against the current merkleRoot. If the merkleRoot is outdated or maliciously altered by the distributionOracle, legitimate users' proofs may fail, preventing them from withdrawing their funds.

4. Potential for Operational Bottlenecks

function performUpkeep(bytes calldata _performData) external {
// Logic to perform withdrawals based on upkeep
// Relies on updated merkleRoot
}

Explanation:
Functions like performUpkeep depend on the accurate and timely update of the merkleRoot. If the oracle fails to update it, the entire upkeep process can stall, leading to a halt in withdrawals and unqueuing operations for all users.

Impact

  • Inaccessibility of Funds: Users may be unable to withdraw or unqueue their tokens due to invalid or outdated Merkle proofs.

  • Operational Bottlenecks: The reliance on a single external oracle can lead to system-wide shutdowns in withdrawal functionalities if the oracle fails.

  • Financial Loss: Users may lose access to their staked funds temporarily or indefinitely, leading to potential financial losses.

  • Reputational Damage: Trust in the stake.link platform can be eroded if users experience issues accessing their funds.

  • Exploitation Opportunities: An attacker targeting the distributionOracle can disrupt the entire withdrawal process, potentially leading to denial-of-service (DoS) attacks.

Tools Used

Manual Review

Recommendations

1. Decentralize Merkle Root Updates

  • Implement Multiple Oracles:

    address[] public distributionOracles;
    modifier onlyDistributionOracles() {
    bool authorized = false;
    for(uint i = 0; i < distributionOracles.length; i++) {
    if(msg.sender == distributionOracles[i]){
    authorized = true;
    break;
    }
    }
    if(!authorized) revert SenderNotAuthorized();
    _;
    }
    function updateDistribution(
    bytes32 _merkleRoot,
    bytes32 _ipfsHash,
    uint256 _amountDistributed,
    uint256 _sharesAmountDistributed
    ) external onlyDistributionOracles {
    merkleRoot = _merkleRoot;
    // Additional update logic...
    }

    Explanation:
    Allowing multiple authorized oracles to update the merkleRoot reduces the risk associated with a single point of failure. It ensures that even if one oracle is compromised or fails, others can continue to maintain the integrity of the merkleRoot.

2. Introduce Community Governance for Critical Updates

  • DAO-Based Updates:

    function proposeMerkleRootUpdate(bytes32 _merkleRoot) external onlyDAO {
    // Proposal logic...
    }
    function executeMerkleRootUpdate(bytes32 _merkleRoot) external onlyDAO {
    merkleRoot = _merkleRoot;
    // Additional update logic...
    }

    Explanation:
    By integrating DAO governance, the community can collectively oversee and approve updates to the merkleRoot. This decentralizes control, enhancing security and trustworthiness.

3. Implement Fallback Mechanisms for Merkle Proofs

  • On-Chain Accounting:

    mapping(address => uint256) public onChainBalances;
    function updateOnChainBalance(address _account, uint256 _balance) external onlyDistributionOracles {
    onChainBalances[_account] = _balance;
    }
    function withdraw(
    uint256 _amountToWithdraw,
    // Other parameters...
    ) external {
    require(onChainBalances[msg.sender] >= _amountToWithdraw, "Insufficient on-chain balance");
    // Proceed with withdrawal without relying solely on Merkle proofs
    }

    Explanation:
    Providing an alternative on-chain method for tracking user balances ensures that withdrawals can still occur even if Merkle proofs are compromised. This redundancy enhances the system's resilience.

4. Enhance Monitoring and Alerting for Oracle Performance

  • Automated Alerts:

    event OracleFailed(address oracle);
    function monitorOracles() external {
    for(uint i = 0; i < distributionOracles.length; i++) {
    // Pseudo-code for monitoring oracle activity
    if(!isOracleResponsive(distributionOracles[i])) {
    emit OracleFailed(distributionOracles[i]);
    // Additional handling logic...
    }
    }
    }

    Explanation:
    Implementing monitoring mechanisms can detect oracle failures promptly, allowing for swift responses such as activating backup oracles or pausing certain functionalities to prevent misuse.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.