Full Report
Scroll is a zero knowledge (ZK) roll up layer 2 blockchain. The idea is to roll up loads of Ethereum transactions on a different blockchain back on to Ethereum. Then, to crank up the privacy, add in a zkEVM to ensure that nobody can see what's going on, while still be provable. The zk rollup system has three phases: Transaction execution Batching and data committment Proof generation and finalization With this structure, if a batch is unprovable, then it would block the whole network. This could lead to hardforks or other bizarre issues. Lag on provability can cause problems as well. The computational power required to do the proving is so expensive that it was not worth setting up. So, they decided to look into the bus mapping module instead. This is responsible for parsing transaction traces generated by L2Geth and converting them into witness data for the zkEVM. By modifying some tests from the project, they got libfuzzer setup. Eventually, they got a crash from doing this. The bug was an out of bounds read within the function get_create_init_code, which can be triggered from the CREATE2 opcode. This is used for finding deterministic address without doing an actual deployment, consisting of a 0xFF, account address, salt provided by the user and bytecode of the contract being deployed. The crash occurred when the CREATE2 opcode was executed twice. This is interesting because it shouldn't be possible to submit two contracts to the same address. The second creation fails due to the contract address collision. During a revert, the proper memory is not allocated to a given user. Since memory expansion occurs but there's not memory that is allocated, a crash occurs. All excited, the team decided to test this attack against a local test node. But, nothing happened. Why? Good design by the Scroll team! They added a circuit capacity checker (CCC) to handle these sorts of cases. In particular, if a transaction is too expensive or crashes, then it is rejected. The execution cost is evaluated before execution. So, unknown attacks can be closed out before hitting the platform. Since the bug was not possible to exploit because of the defense in depth measures, they didn't get a pay out. The Scroll team fixed the memory corruption bug though. To me, if there's a defense-in-depth measure that blocks an attack, a payout should still be made but just a lesser amount. If there's XSS that's blocked by the CSP, you pay for the XSS! The CSP bypass is a separate security finding to me. Regardless, good write up!
Analysis Summary
# Vulnerability: Out-of-Bounds Read in Scroll zkEVM Bus Mapping Module (CREATE2 Opcode Interaction)
## CVE Details
- CVE ID: **Not explicitly assigned in the provided text**
- CVSS Score: **Not explicitly provided**
- CWE: Could align with CWE-125 (Out-of-bounds Read)
## Affected Systems
- Products: Scroll zk Rollup blockchain (specifically the bus mapping module responsible for parsing transaction traces for the zkEVM).
- Versions: Not specified, but implies versions existing before the fix.
- Configurations: Triggers when the `CREATE2` opcode is executed twice against the same address slot, leading to a revert path where memory is insufficiently allocated.
## Vulnerability Description
The vulnerability is an **out-of-bounds read** found within the function `get_create_init_code`. This bug is triggered when the `CREATE2` opcode is executed twice, attempting to deploy a contract to the exact same address. A second creation attempt should fail due to address collision. However, during the resulting transaction **revert path**, the system fails to properly allocate memory for the user context, leading to a crash (memory corruption). This crash could potentially block the network if it occurred on an unprovable batch.
## Exploitation
- Status: **Not exploited (Blocked)**. The vulnerability was discovered via fuzzing (`libfuzzer`) but could not be practically exploited against the mainnet as the attack was blocked by a prior defense mechanism.
- Complexity: **Medium** (Required modifying tests to set up fuzzing, triggering the specific double `CREATE2` scenario).
- Attack Vector: **Network** (Via submitting malicious transaction batches).
## Impact
- Confidentiality: Potential (If memory corruption leads to subsequent data leaks, though not confirmed by the text).
- Integrity: High (Could lead to a network blockage/crash if the unprovable batch state was finalized).
- Availability: High (Potential for network blockage/hardfork due to an unprovable batch).
## Remediation
### Patches
- The specific patch is not detailed, but the description implies that the Scroll team **fixed the memory corruption bug** in the relevant module, likely related to memory allocation during revert paths following a `CREATE2` collision sequence.
### Workarounds
- The primary workaround that prevented exploitation was the **Circuit Capacity Checker (CCC)** implemented by Scroll. This defense mechanism evaluates execution cost preemptively and rejected transactions that were deemed too expensive or likely to crash, effectively closing the attack vector before it reached the core proving system.
## Detection
- **Indicators of Compromise (IOCs):** Transactions resulting in a formal state revert specifically linked to a `CREATE2` collision sequence followed immediately by another `CREATE2` attempt targeting the same computed address. Monitoring logs for unexpected memory allocation failures during transaction reverts.
- **Detection Methods and Tools:** Fuzzing tools configured to specifically test double `CREATE2` operations (as used by the researchers) are effective detection methods. On-chain monitoring for transactions hitting the CCC rejection threshold related to expensive execution paths.
## References
- [Vendor Advisories: None provided in text]
- [Relevant links: None provided in text]