DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: medium
Invalid

Incorrect Storage Slot Hashing Breaks Profit Unlock Time Verification and Disables Cross-Chain Oracle Updates

Summary

A critical vulnerability has been identified in the ScrvusdVerifierV2 contract's _extractPeriodFromProof function. The function incorrectly attempts to access the profit_max_unlock_time storage variable from the scrvUSD contract by using an invalid storage slot hash calculation method. This error would cause state proof verification to fail, rendering the contract's core functionality unusable.

Vulnerability Details

The affected code is in the _extractPeriodFromProof function:

function _extractPeriodFromProof(
bytes32 stateRoot,
bytes memory proofRlp
) internal view returns (uint256) {
RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();
require(proofs.length == 2, "Invalid number of proofs");
// Extract account proof
Verifier.Account memory account = Verifier.extractAccountFromProof(
ScrvusdVerifierV1.SCRVUSD_HASH,
stateRoot,
proofs[0].toList()
);
require(account.exists, "scrvUSD account does not exist");
Verifier.SlotValue memory slot = Verifier.extractSlotValueFromProof(
keccak256(abi.encode(PERIOD_SLOT)),
account.storageRoot,
proofs[1].toList()
);
require(slot.exists);
return slot.value;
}

The vulnerability is in this line:

keccak256(abi.encode(PERIOD_SLOT))

Impact

The vulnerability prevents accurate verification of state proofs for the profit_max_unlock_time parameter. When the contract attempts to verify a proof using this incorrect slot calculation, the Merkle proof validation will fail even with correctly constructed proofs. This failure occurs because the calculated storage slot hash doesn't correspond to the actual storage location of the profit_max_unlock_time variable in the scrvUSD contract's storage trie.

The impact propagates through the verification workflow, causing all calls to verifyPeriodByBlockHash and verifyPeriodByStateRoot to revert when they attempt to validate submitted proofs. Consequently, the system becomes unable to process valid updates to the critical profit max unlock time parameter, effectively disabling the core functionality of the ScrvusdVerifierV2 contract in the protocol's oracle system.

Root Cause

The vulnerability stems from a misunderstanding of how Ethereum storage slots are accessed:

  1. For direct storage variables at fixed slots (like slot 37), a simple conversion from the slot number to bytes32 is required

  2. The current implementation incorrectly applies a keccak256 hash to the slot number

  3. The abi.encode() function adds additional encoding that further modifies the resulting hash

Fix Recommendation

Replace the incorrect storage slot calculation with the correct approach:

// INCORRECT:
keccak256(abi.encode(PERIOD_SLOT))
// CORRECT:
bytes32(uint256(PERIOD_SLOT))

The corrected function should look like:

function _extractPeriodFromProof(
bytes32 stateRoot,
bytes memory proofRlp
) internal view returns (uint256) {
RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();
require(proofs.length == 2, "Invalid number of proofs");
// Extract account proof
Verifier.Account memory account = Verifier.extractAccountFromProof(
ScrvusdVerifierV1.SCRVUSD_HASH,
stateRoot,
proofs[0].toList()
);
require(account.exists, "scrvUSD account does not exist");
Verifier.SlotValue memory slot = Verifier.extractSlotValueFromProof(
bytes32(uint256(PERIOD_SLOT)),
account.storageRoot,
proofs[1].toList()
);
require(slot.exists);
return slot.value;
}

Additionally, verify that the slot number (37) is indeed correct for accessing profit_max_unlock_time in the scrvUSD contract, as Vyper's storage layout may differ from Solidity's.

Updates

Lead Judging Commences

0xnevi Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[invalid] finding-storage-key-compute-wrong

See primary comments in issue #23

Support

FAQs

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