-
Notifications
You must be signed in to change notification settings - Fork 10
xiaoming90 - Malicious Withdrawals Might Be Migrated #246
Comments
Comment from Optimism Description: Withdrawals not from the L2 Crossdomain Messenger halt the migration process Reason: When migrationData.ToWithdrawals() is called, any tx that isn’t from L2CrossDomainMessenger will throw an error. This will cause a halt of the migration process. |
I suspect that this issue may in fact be a duplicate of #105, and IMO should be subject to escalation without staking. |
Hi @maurelian, I have a look at Issue 105. I would like to clarify that this issue is not a duplicate of Issue 105. For issue 105, the report highlights a bug in the File: legacy_withdrawal.go
52: // Decode will decode a serialized LegacyWithdrawal
53: func (w *LegacyWithdrawal) Decode(data []byte) error {
54: if len(data) < len(predeploys.L2CrossDomainMessengerAddr)+4 {
55: return fmt.Errorf("withdrawal data too short: %d", len(data))
56: }
57:
58: selector := crypto.Keccak256([]byte("relayMessage(address,address,bytes,uint256)"))[0:4]
59: if !bytes.Equal(data[0:4], selector) {
60: return fmt.Errorf("invalid selector: 0x%x", data[0:4])
61: }
62:
63: msgSender := data[len(data)-len(predeploys.L2CrossDomainMessengerAddr):]
64: if !bytes.Equal(msgSender, predeploys.L2CrossDomainMessengerAddr.Bytes()) {
65: return errors.New("invalid msg.sender")
66: } However, in this report, it is highlighting a bug in the File: types.go
120: func (m *MigrationData) ToWithdrawals() ([]*crossdomain.LegacyWithdrawal, error) {
121: messages := make([]*crossdomain.LegacyWithdrawal, 0)
122: for _, msg := range m.OvmMessages {
123: wd, err := msg.ToLegacyWithdrawal()
124: if err != nil {
125: return nil, err
126: }
127: messages = append(messages, wd)
128: if err != nil {
129: return nil, err
130: }
131: }
132: for _, msg := range m.EvmMessages {
133: wd, err := msg.ToLegacyWithdrawal()
134: if err != nil {
135: return nil, err
136: }
137: messages = append(messages, wd)
138: }
139: return messages, nil
140: } These are two distinct bugs found in two different contracts. As such, they should be rewarded separately. We should not group all bugs that halt the migration under one issue, unless the remediation action to fix the issue is the same. If the OP team only refers to Issue 105, they would only patch the bug in the To prevent someone from halting the migration process, the OP team would also need to fix the bug highlighted in this report, which is to patch the bug in the I hope that help to clarify the distinction between Issue 105 and this report. Let me know if any further clarification is needed. Thanks. |
Hi @xiaoming9090 — can you clarify which bug in As far as I can tell, Instead, the reason it crashes is that |
Downgrading to false as this attack describe is not possible, and the proposed attack will instead lead to the migration crash described on #105 (and the Watson for this issue already submitted #232, which is an approved dup of #105). Here is the explanation of why the attack is not valid:
|
xiaoming90
high
Malicious Withdrawals Might Be Migrated
Summary
Malicious withdrawals might be migrated, which allows arbitrary messages to be relayed, leading to loss of funds.
Vulnerability Detail
Section 1 - About State Dump File
In pre-bedrock, when someone calls
OVM_L2ToL1MessagePasser.passMessageToL1
function, the followingCall
function in theevm.go
will be triggered. Then, the caller address and transaction data will be written to the state dump file at Line 208.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/l2geth/core/vm/evm.go#L202
The following new line consists of the caller address (sender) and transaction data (msg) will be added to the state dump file located at
L2GETH_STATE_DUMP_PATH
environment variable.MSG|<sender>|<msg>
Section 2 - Converting State Dump File To 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#L17
The generated
evm-messages.json
JSON file will have the following format:Section 3 - Converting JSON files to migration data (ovmMessages, evmMessages)
During the migration, the
evm-messages.json
JSON file will be loaded, unmarshalled, and stored in theevmMessages
migration data variable.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/genesis/migration_action/action.go#L30
Section 4 - Performing migration process
During the migration, the legacy withdrawals from the legacy
LegacyMessagePasser
contract will be migrated to their new format in the BedrockL2ToL1MessagePasser
contract via thecrossdomain.MigrateWithdrawals
function at Line 188.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/genesis/db_migration.go#L187
The
MigrateWithdrawal
function below is responsible for turning a legacy withdrawal into a bedrock-style withdrawal. An important point to note here is that the new bedrock style withdrawal is created with thesender
hardcoded to&predeploys.L2CrossDomainMessengerAddr
and thetarget
hardcoded asl1CrossDomainMessenger
as per Line 90-91 below.Therefore, the original
sender
andtarget
in the legacy withdrawal will be discarded and replaced with the&predeploys.L2CrossDomainMessengerAddr
andl1CrossDomainMessenger
respectively. As a result, anyone can submit a legacy withdrawal before the migration and the system will consider it originated fromL2CrossDomainMessengerAddr
, thus providing an opportunity for malicious users to perform spoofing attacks. This is the root cause of the issue discussed later.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/crossdomain/migrate.go#L54
After converting the legacy withdrawal into a bedrock-style withdrawal, it will be inserted into the storage of Bedrock's
L2ToL1MessagePasser
contract. This will ensure that the relayers can prove the withdrawal via theOptimismPortal.proveWithdrawalTransaction
after the migration.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/crossdomain/migrate.go#L22
Section 5 - Permissionless
OVM_L2ToL1MessagePasser.passMessageToL1
functionBefore the migration (pre-bedrock), anyone can call the
passMessageToL1
function within the Message Passer Contract (OVM_L2ToL1MessagePasser) to submit a withdrawal. The transaction data (_message) and the caller address (msg.sender) will be encoded and hashed to produce a key for thesentMessages
mapping.As mentioned in the comments, there is a mechanism within the legacy
L1CrossDomainMessenger._verifyStorageProof()
contract to ensure that only messages sent fromL2CrossDomainMessenger
will be relayed before the migration (pre-bedrock). An important point to note is that this mechanism is no longer used in bedrock after the migration.https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol#L29
Section 6 - Crafting Malicious Payload
Assume that the following data will be the payload passes to the
OVM_L2ToL1MessagePasser.passMessageToL1
function.The
_target
is set toL1Bridge
address and the_sender
is set toL2Bridge
address. The_message
will be set to as follows:The
_to
is set to the attacker's wallet and_amount
is set to a sufficiently large ETH amount that the L1 bridge is expected to hold.Section 7 - Exploitation
Right before the migration start, the attacker can submit the malicious payload in the previous section to the
OVM_L2ToL1MessagePasser.passMessageToL1
function. When the migration starts, the attacker's legacy withdrawal will be migrated to the bedrock-style withdrawal, and the sender and target of the malicious withdrawal will be set to&predeploys.L2CrossDomainMessengerAddr
andl1CrossDomainMessenger
respectively.After the migration, the attacker can call the prove the malicious withdrawal via the
OptimismPortal.proveWithdrawalTransaction
. Since the malicious withdrawal has already been inserted into the storage of Bedrock'sL2ToL1MessagePasser
contract during the migration process (Refer to Section 4), the attacker will have no issue proving the malicious withdrawal.After the 7-day waiting period, the attacker can call the
OptimismPortal.finalizeWithdrawalTransaction
to finalize the withdrawal, and the Optimism Portal will call theL1CrossDomainMessenger.relayMessage
function to relay the message within the withdrawal. TheL1CrossDomainMessenger
will in turn call theL1StandardBridge.finalizeBridgeETH
function to finalize the ETH bridging, and the ETH will be forwarded to the attacker's address.At this point, the attacker successfully steals ETH Optimism's L1 infrastructure. Note that the attacker could also drain any ERC20 tokens held by Optimism's L1 infrastructure by switching the selector within the malicious payload to
finalizeBridgeERC20
instead.Impact
The impact is High as it results in loss of funds.
Code Snippet
https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/op-chain-ops/genesis/migration/types.go#L120
Tool used
Manual Review
Recommendation
It is recommended only to migrate legacy withdrawals initiated by Legacy's L2CrossDomainMessenger.
Update the
ToWithdrawals
function to only process legacy withdrawals initiated by Legacy's L2CrossDomainMessenger.The text was updated successfully, but these errors were encountered: