Noisy Quartz Bird - Storage Manipulation Attack using _version
in initialization process
#59
Labels
Sponsor Disputed
The sponsor disputed this issue's validity
Noisy Quartz Bird
High
Storage Manipulation Attack using
_version
in initialization processSummary
https://github.com/sherlock-audit/2025-01-perennial-v2-4-update/blob/main/perennial-v2/packages/core/contracts/Market.sol#L120-L128
_version
is critical to upgrade security.If it is reset, manipulated, or bypassed, an attacker can take over the contract via re-initialization or forced downgrades.
If
_version
is stored in an upgradeable proxy’s storage, an attacker could overwrite it and re-triggerinitialize()
, bypassing security.This attack is a serious risk in upgradeable contracts.
Many real-world exploits involve storage overwrites, making
_version
a critical weakness.https://solodit.cyfrin.io/issues/changing-_initializableslot-can-cause-_disableinitializers-to-actually-enable-initializers-spearbit-none-coinbase-solady-pdf
PoC
Since
_version
is used to track the initialization state, if an attacker finds a way to corrupt or reset _version, they can re-trigger initialize().Attack Steps
Find a Storage Collision or Overwrite
_version
If
_version
is stored in an upgradeable proxy contract, an attacker could override this slot by deploying a malicious contract that writes over the same storage index.Example Attack Contract:
This forces
_version = 0
, bypassing the initializer(1) check.Call
initialize()
AgainAfter corrupting
_version
, the attacker calls initialize() again to replace the token and oracle with malicious versions.Outcome
The attacker resets initialization.
They replace the token with a malicious ERC-20 that allows infinite minting.
They set oracle to a fake price feed, allowing market manipulation.
They drain all user funds by exploiting liquidity rules.
Recommendation
How to Fix This?
_version
in an Immutable ContractIf
_version
is only stored in a proxy contract, it becomes easier to overwrite.Moving
_version
into an immutable logic contract removes this attack vector.Double-Lock Initialization with a Boolean Flag
This prevents any form of re-initialization.
Use Explicit Storage Slots for Upgradeable Contracts
Instead of keccak256("some string"), explicitly define storage slots to prevent overwrites.
2. Use a Hard Upgrade Lock
Blocks reverting to older versions.
_version
Before UpgradingThe text was updated successfully, but these errors were encountered: