You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on May 26, 2023. It is now read-only.
github-actionsbot opened this issue
Feb 20, 2023
· 0 comments
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
The migration process can be DOSed by anyone causing reputation damage, delaying the migration, and causing griefing to the OP team.
Vulnerability Detail
Section 1 - OVM_L2ToL1MessagePasser.passMessageToL1 function
In pre-bedrock, OVM_L2ToL1MessagePasser.passMessageToL1 function is permissionless. Thus, anyone can call this function with an arbitrary _message. Under normal circumstances, it is expected that only the L2CrossDomainMessenger will call this function. However, since it is permissionless, malicious users can call this function to insert withdrawal into the storage slot of the OVM_L2ToL1MessagePasser contract, which will prove to be problematic later during the migration.
Subsequently, the caller's address and their _message will be hashed and encoded to generate the transaction hash to be used as the key for the sentMessages mapping. The contract's sentMessages mapping is used to verify the existence of the transaction hash that the user has submitted.
File: OVM_L2ToL1MessagePasser.sol
29: function passMessageToL1(bytesmemory_message) public {
30: // Note: although this function is public, only messages sent from the31: // L2CrossDomainMessenger will be relayed by the L1CrossDomainMessenger.32: // This is enforced by a check in L1CrossDomainMessenger._verifyStorageProof().33: sentMessages[keccak256(abi.encodePacked(_message, msg.sender))] =true;
34: }
Section 2 - About State Dump File
When someone calls OVM_L2ToL1MessagePasser.passMessageToL1 function, the following Call function in the evm.go will be triggered. Within the Call function, it will call the WriteMessage function of the state dumper at Line 208.
File: evm.go202: // Call executes the contract associated with the addr with the given input as203: // parameters. It also handles any necessary value transfer required and takes204: // the necessary steps to create accounts and reverses the state in case of an205: // execution error or failed value transfer.206: func (evm*EVM) Call(callerContractRef, addr common.Address, input []byte, gasuint64, value*big.Int) (ret []byte, leftOverGasuint64, errerror) {
207: ifaddr== dump.MessagePasserAddress {
208: statedumper.WriteMessage(caller.Address(), input)
209: }
The caller address and transaction data pass into the WriteMessage function and these data will be written to the state dump file located at L2GETH_STATE_DUMP_PATH environment variable.
The following new line consists of the caller address (sender) and transaction data (msg) will be added to the state dump file.
MSG|<sender>|<msg>
Assume that Alice's address is 0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b. If she calls the OVM_L2ToL1MessagePasser.passMessageToL1 function with an arbitrary message, the following line will be found in the state dump file.
Referring back to the same example in the previous section, assume that Alice's address is 0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b, then one of the lines within the state dumper file will be as follows:
Almost all of the SentMessageJSON objects will have their who key set to 0x4200000000000000000000000000000000000007 as this is the address of the L2CrossDomainMessenger.
However, there is one (1) SentMessageJSON object that has its who key set to 0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b which is Alice's address. Note that this one JSON object will be the reason that breaks the migration process.
Section 4 - Execution Will Abort During Migration
During the migration, it will attempt to convert a SentMessageJSON object to a LegacyWithdrawal struct as shown below. The Decode function will be called during the conversion.
File: types.go40: // ToLegacyWithdrawal will convert a SentMessageJSON to a LegacyWithdrawal41: // struct. This is useful because the LegacyWithdrawal struct has helper42: // functions on it that can compute the withdrawal hash and the storage slot.43: func (s*SentMessage) ToLegacyWithdrawal() (*crossdomain.LegacyWithdrawal, error) {
44: data:=make([]byte, len(s.Who)+len(s.Msg))
45: copy(data, s.Msg)
46: copy(data[len(s.Msg):], s.Who[:])
47:
48: varw crossdomain.LegacyWithdrawal49: iferr:=w.Decode(data); err!=nil {
50: returnnil, err51: }
52: return&w, nil53: }
Within the Decode function, Line 64 will check if the sender of the withdrawal is initiated by L2CrossDomainMessenger. If not, the function will return an error and the execution will halt.
Recall that in our example, one of the withdrawals is initiated by Alice. Thus, this will cause the Decode function to return an error, and the migration process will stop.
Malicious users or competitors could attempt to DOS the migration process causing reputation damage, delaying the migration, and causing griefing to the OP team.
Understood from the sponsor that only legacy withdrawals initiated by the L2CrossDomainMessenger will be migrated.
Consider updating the script to push only legacy withdrawals initiated by L2CrossDomainMessenger to the JSON file (evm-messages.json). This ensures that the JSON file only contains legacy withdrawals initiated by L2CrossDomainMessenger so that it is impossible for the decoding process to error out during the migration.
program
.command('parse-state-dump')
.description('parses state dump to json')
.option('--file <file>', 'path to state dump file')
.action(async (options) => {
const iface = getContractInterface('OVM_L2ToL1MessagePasser')
const dump = fs.readFileSync(options.file, 'utf-8')
const addrs: string[] = []
const msgs: any[] = []
for (const line of dump.split('\n')) {
if (line.startsWith('ETH')) {
addrs.push(line.split('|')[1].replace('\r', ''))
} else if (line.startsWith('MSG')) {
const msg = '0x' + line.split('|')[2].replace('\r', '')
const parsed = iface.decodeFunctionData('passMessageToL1', msg)
+ const who = line.split('|')[1]++ if (who == 0x4200000000000000000000000000000000000007) {
msgs.push({
+ who: who,- who: line.split('|')[1],
msg: parsed._message,
})
+ }
}
}
fs.writeFileSync(
'./data/evm-addresses.json',
JSON.stringify(addrs, null, 2)
)
fs.writeFileSync('./data/evm-messages.json', JSON.stringify(msgs, null, 2))
})
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
xiaoming90
medium
Migration Process Can Be DOSed By Anyone
Summary
The migration process can be DOSed by anyone causing reputation damage, delaying the migration, and causing griefing to the OP team.
Vulnerability Detail
Section 1 -
OVM_L2ToL1MessagePasser.passMessageToL1
functionIn pre-bedrock,
OVM_L2ToL1MessagePasser.passMessageToL1
function is permissionless. Thus, anyone can call this function with an arbitrary_message
. Under normal circumstances, it is expected that only theL2CrossDomainMessenger
will call this function. However, since it is permissionless, malicious users can call this function to insert withdrawal into the storage slot of theOVM_L2ToL1MessagePasser
contract, which will prove to be problematic later during the migration.Subsequently, the caller's address and their
_message
will be hashed and encoded to generate the transaction hash to be used as the key for thesentMessages
mapping. The contract'ssentMessages
mapping is used to verify the existence of the transaction hash that the user has submitted.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol#L29
Section 2 - About State Dump File
When someone calls
OVM_L2ToL1MessagePasser.passMessageToL1
function, the followingCall
function in theevm.go
will be triggered. Within theCall
function, it will call theWriteMessage
function of the state dumper at Line 208.https://github.com/sherlock-audit/2023-01-optimism/blob/main/op-geth/core/vm/evm.go
The caller address and transaction data pass into the
WriteMessage
function and these data will be written to the state dump file located atL2GETH_STATE_DUMP_PATH
environment variable.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/l2geth/statedumper/dumper.go#L55
The following new line consists of the caller address (sender) and transaction data (msg) will be added to the state dump file.
Assume that Alice's address is
0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b
. If she calls theOVM_L2ToL1MessagePasser.passMessageToL1
function with an arbitrary message, the following line will be found in the state dump file.Section 3 - Converting State Dump File To
SentMessageJSON
objects in JSON Files (evm-messages.json)Before the migration, the following script will be executed to parse the state dump files and generate the
evm-messages.json
migration data.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/migration-data/bin/cli.ts#L18
The generated
evm-messages.json
JSON file will have the following format:Referring back to the same example in the previous section, assume that Alice's address is
0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b
, then one of the lines within the state dumper file will be as follows:As such, the generated
evm-messages.json
JSON file will be as follows:Almost all of the
SentMessageJSON
objects will have theirwho
key set to0x4200000000000000000000000000000000000007
as this is the address of theL2CrossDomainMessenger
.However, there is one (1)
SentMessageJSON
object that has itswho
key set to0x536fbBaE279fd77FAe3E29b410f7B605bf45BC8b
which is Alice's address. Note that this one JSON object will be the reason that breaks the migration process.Section 4 - Execution Will Abort During Migration
During the migration, it will attempt to convert a
SentMessageJSON
object to aLegacyWithdrawal
struct as shown below. TheDecode
function will be called during the conversion.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/genesis/migration/types.go#L43
Within the
Decode
function, Line 64 will check if the sender of the withdrawal is initiated byL2CrossDomainMessenger
. If not, the function will return an error and the execution will halt.Recall that in our example, one of the withdrawals is initiated by Alice. Thus, this will cause the
Decode
function to return an error, and the migration process will stop.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/crossdomain/legacy_withdrawal.go#L53
Impact
Malicious users or competitors could attempt to DOS the migration process causing reputation damage, delaying the migration, and causing griefing to the OP team.
Code Snippet
https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/migration-data/bin/cli.ts#L18
Tool used
Manual Review
Recommendation
Understood from the sponsor that only legacy withdrawals initiated by the L2CrossDomainMessenger will be migrated.
Consider updating the script to push only legacy withdrawals initiated by L2CrossDomainMessenger to the JSON file (evm-messages.json). This ensures that the JSON file only contains legacy withdrawals initiated by L2CrossDomainMessenger so that it is impossible for the decoding process to error out during the migration.
Duplicate of #105
The text was updated successfully, but these errors were encountered: