ImmutableSimulator
is breaking contract immutability axiom
#336
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
edited-by-warden
primary issue
Highest quality submission among a set of duplicates
sponsor disputed
Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue
sufficient quality report
This report is of sufficient quality
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/ImmutableSimulator.sol#L41
Vulnerability details
Impact
Theoretical discussion
On Ethereum (L1 EVM), immutables become part of the stored contract's bytecode which makes their immutability and therefore the contract's immutability an absolute mathematical certainty. Except for the known selfdestruct pattern which is not possible on zkSync Era.
On zkSync Era (L2 EVM), the immutables of all contracts that are ever going to be deployed there share one contract storage for all their immutables using the ImmutableSimulator contract.
The immutables are stored within a 2-dimensional mapping, see setImmutables(...):
Even though the chance of storage collisons is astronomically low due to the mapping's underlying
keccak256
hashing, it is not 0. As a consequence, contract immutability is not an absolute mathematical certainty anymore on zkSync Era which should be a fundamental property of any EVM-equivalent implementation.Fortunately, this can be solved in an efficient & straightforward way by using an unique storage slot for each immutable of each contract ever going to be deployed on zkSync Era:
As a result, contract immutability is now also a fundamental property with absolute mathematical certainty on zkSync Era.
Attack vector
Currently, the following is true:
ImmutableSimulator
contract that requires the value of a storage slot to be 0 before being (over-)written. Assuming 0 means uninitialized.As a result, a potential attacker has 2 variables to play with:
create2
with a salt.At this point it is only a question of time, computational power and economical incentives to overwrite an existing contract's immutable value.
Using the storage slot computation, which was proposed above, would completely close this attack vector.
Conclusion
Irrespective of technical feasibility of an exploit, contract immutability can be considered as an asset of an EVM-equivalent implementation and therefore deserves to be preserved with absolute mathematical certainty.
Additional reference
Concerning collisions, there is an interesting discussion about them in EIP-3607. As a result, EIP-3607 was merged into the Ethereum yellow paper and the Geth execution client, which further supports the validity of the current concern.
Proof of Concept
The following PoC demonstrates, that an immutables index can be arbitrarily chosen.
The following contract has 3 immutables with the values 1, 1 and 2 which the compiler places at index 0, 32 and 64.
The following test shows how the index of the 3rd immutable is changed from 64 (0x40) to 0xFFFF by manipulating the bytecode (the easy way).
Apply the diff to the existing
ImmutableSimulator
test suite and runbash quick-setup.sh
from./code/system-contracts/scripts
to execute the PoC test case:Tools Used
Manual review
Recommended Mitigation Steps
See above.
Assessed type
Other
The text was updated successfully, but these errors were encountered: