Disclosing two fault proof system vulnerabilities

Summary

  • We discovered and fixed two medium severity vulnerabilities impacting OP Stack chains that run a permissionless fault proof system.

  • Neither were exploited on any OP Stack chain.

  • Both issues were fixed in the U17 upgrade, no further action is needed for chains that have upgraded to U17 and activated the Jovian hard fork.

Vulnerability overview

Both vulnerabilities relate to L2 transactions interacting with accelerated precompiles that are too large to be fault proven on L1:

  1. ecrecover: Precompiles with variable-length inputs that are accelerated via the preimage oracle have their inputs limited on L2 so that they’re guaranteed to fit into an L1 transaction (which is needed for acceleration via the preimage oracle). The ecrecover precompile does not have variable-length input, but we discovered that it was still possible to give it extremely large input which is ignored in the L2 transaction. If this input was larger than the current L1 gas limit then the L1 transaction to the preimage oracle would fail, and the precompile result would not be available to the fault proof program. This impacted all chains using permissionless fault proofs deployed on both Mainnet and Sepolia.

  2. Fusaka per-transaction gas limits: The Fusaka Ethereum upgrade introduces a new limit on the maximum gas that can be used by a single transaction. If an attacker crafted an L2 transaction using a precompile that’s accelerated for the fault proof VM, and gave inputs larger than the new L1 transaction size, the precompile result would not have been available in the preimage oracle causing the fault proof VM execution to fail. This impacted chains relying on permissionless fault proofs deployed on Sepolia (Mainnet was not vulnerable.)

We classified these bugs as a medium severity issue because they fall into the following class of vulnerability as per our Immunefi program:

Incorrectly resolved dispute game (mitigated by a delay AND requires calling FaultDisputeGame.step() against a claim at MAX_GAME_DEPTH or calling FaultDisputeGame.attack or FaultDisputeGame.defend against a claim at MAX_GAME_DEPTH-2).

Exploiting them would have required an investment of ~300 ETH from an attacker and would not represent an immediate and urgent danger even if an attacker were to attempt to execute against the bug because the issue sits behind the “airgap” such that the dispute game can be easily detected and invalidated if necessary.

Both of these were fixed with the Jovian hard fork (Upgrade 17). Standard dispute game monitoring would have detected any games that resolved incorrectly if an attacker were to attempt to exploit this issue on Sepolia networks.

Context for evaluating fault proof vulnerabilities

  • Our security posture for fault proofs focuses on fundamental safety mechanisms first – we take a defense in depth approach, assuming that bugs in the fault proof system do exist.

  • Together with chain operators, there are both protocol-level and operational safeguards in place: monitoring, the air gap, the ability to blacklist games, and potentially fall back to permissioned dispute games.

  • It’s very expensive for an adversary to attempt to exploit a fault proof vulnerability if it requires playing a full dispute game down to the step() function (they will require ~300 ETH in bonds which they will forfeit if they don’t win the dispute game), so they only have incentive to do so if they know they can succeed. So knowing that we can blacklist games is a strong deterrent.

8 Likes