zkSync Era - Audittens's results

Future-proof zkEVM on the mission to scale freedom for all.

General Information

Platform: Code4rena

Start Date: 02/10/2023

Pot Size: $1,100,000 USDC

Total HM: 28

Participants: 64

Period: 21 days

Judge: GalloDaSballo

Total Solo HM: 13

Id: 292

League: ETH

zkSync

Findings Distribution

Researcher Performance

Rank: 3/64

Findings: 8

Award: $140,480.79

🌟 Selected for report: 3

πŸš€ Solo Findings: 3

Findings Information

🌟 Selected for report: Koolex

Also found by: Audittens, rvierdiiev

Labels

bug
2 (Med Risk)
low quality report
satisfactory
sponsor confirmed
duplicate-979

Awards

6776.3633 USDC - $6,776.36

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1667-L1669 https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1681 https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L991-L996

Vulnerability details

Description

The issue arises from the following sequence in the bootloader's behavior:

  1. The ZKSYNC_NEAR_CALL_executeL1Tx function is responsible for executing an L1->L2 transaction.
  2. Within this function, if the L1 transaction fails (success = 0), the nearCallPanic() function is invoked.
  3. The nearCallPanic() function, when called, exhausts all the remaining gas for the current frame.
  4. Consequently, in getExecuteL1TxAndGetRefund function, potentialRefund is equal to zero.
  5. As a result, user doesn't receive the expected gas refund, diverging from Ethereum's behavior where unused gas is returned to the caller upon a transaction failure.

Impact

  • Users end up paying more than expected for failed transactions as they do not receive the expected gas refunds.
  • Ethereum incompatibility: the described behavior is inconsistent with the Ethereum standards, leading to potential compatibility issues for dApps and systems expecting Ethereum-like behavior.

Modify the getExecuteL1TxAndGetRefund function. First, calculate and store the value of gasSpentOnExecution in memory. Only after storing this value should the nearCallPanic() function be invoked. By doing this, even if nearCallPanic() exhausts all the remaining gas, the gasSpentOnExecution stored in memory can be used to correctly compute the potentialRefund, ensuring compatibility with Ethereum's behavior.

Assessed type

Other

#0 - c4-pre-sort

2023-10-31T07:08:15Z

bytes032 marked the issue as low quality report

#1 - miladpiri

2023-11-08T14:15:23Z

True, but the operator can provide the additional refund. This is duplicate. Low/Medium.

#2 - c4-sponsor

2023-11-08T14:15:55Z

miladpiri (sponsor) confirmed

#3 - c4-judge

2023-11-28T12:55:19Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#4 - GalloDaSballo

2023-11-28T12:56:14Z

I believe I have judged similar findings, ultimately L1->L2 is not equivalent to any specific L1 TX and in case of a revert, the gas is consumed

I believe this can be argued in many ways

And one way is that this prevents spam of the L2

Downgrading to QA but open to hearing about higher impacts

#5 - Barichek

2023-12-04T12:11:52Z

@GalloDaSballo @vladbochok

this prevents spam of the L2

The argument that retaining the entire gas limit on failed transactions prevents spam on L2 is not technically substantiated. Firstly, spam prevention in blockchain networks primarily relies on the cost incurred for computational resources actually used, not the total gas limit set. In a scenario where a transaction reverts, only the gas corresponding to computational resources consumed (gas_spent) should be charged. The remaining gas (difference between gas_limit and gas_spent) plays no role in spam prevention, as it represents unused resources.

Secondly, if an actor intends to spam the L2 network, they can simply set a lower gas limit close to the expected consumption, thereby minimizing their cost while still overloading the network. The current mechanism does not deter such behavior. Instead, it primarily impacts unaware users who set higher gas limits, expecting standard Ethereum-like refunds on failed transactions. This leads to unnecessary financial penalties without providing additional protection against spam.

Therefore, the retention of the full gas limit in the case of failed L1 to L2 transactions does not effectively contribute to spam prevention on the L2 network and could be reconsidered for a more balanced approach.

#6 - c4-judge

2023-12-10T18:37:19Z

This previously downgraded issue has been upgraded by GalloDaSballo

#7 - c4-judge

2023-12-10T18:37:19Z

This previously downgraded issue has been upgraded by GalloDaSballo

#8 - c4-judge

2023-12-10T18:37:33Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#9 - c4-judge

2023-12-10T18:39:17Z

This previously downgraded issue has been upgraded by GalloDaSballo

#10 - c4-judge

2023-12-10T18:39:17Z

This previously downgraded issue has been upgraded by GalloDaSballo

#11 - c4-judge

2023-12-10T18:39:55Z

GalloDaSballo marked the issue as duplicate of #979

#12 - c4-judge

2023-12-10T18:40:04Z

GalloDaSballo marked the issue as satisfactory

Findings Information

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
duplicate-888

Awards

656.3255 USDC - $656.33

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/Constants.sol#L35 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/AccountCodeStorage.sol#L93-L95 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/AccountCodeStorage.sol#L131-L137

Vulnerability details

Description

CURRENT_MAX_PRECOMPILE_ADDRESS is set to uint256(uint160(SHA256_SYSTEM_CONTRACT)) which is address(0x02), while the real maximal used precompile address is ECMUL_SYSTEM_CONTRACT = address(0x07).

The AccountCodeStorage::getCodeHash and AccountCodeStorage::getCodeSize functions rely on this constant to emulate the Ethereum behavior of the corresponding opcodes.

Impact

The wrong value is returned in the AccountCodeStorage::getCodeHash and AccountCodeStorage::getCodeSize functions for the precompiles that were added with addresses greater than address(0x02).

Set the CURRENT_MAX_PRECOMPILE_ADDRESS constant to be equal to ECMUL_SYSTEM_CONTRACT or rename the mentioned constant to be MAXIMAL_POSSIBLE_PRECOMPILE_ADDRESS (and set it to the upper bound for the possible precompiles addresses).

Assessed type

Other

#0 - c4-pre-sort

2023-10-31T11:36:37Z

bytes032 marked the issue as duplicate of #142

#1 - c4-pre-sort

2023-10-31T11:36:44Z

bytes032 marked the issue as sufficient quality report

#2 - c4-judge

2023-11-23T19:31:00Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: Audittens

Labels

bug
2 (Med Risk)
low quality report
selected for report
sponsor confirmed
M-07

Awards

32626.9346 USDC - $32,626.93

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1244-L1245 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1255-L1259 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1365 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1542 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/scripts/process.ts#L123 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/Compressor.sol#L54-L82

Vulnerability details

Description

There is enforcement in the bootloader::sendCompressedBytecode function that the data to be used for the call of the Compressor::publishCompressedBytecode function is encoded in a standard way.

But this check contains bugs:

  1. There is no check that the originalBytecodeOffset is equal to afterSelectorPtr + 64. This leads to the possibility of passing a great number of unused bytes as calldata of the call of the Compressor::publishCompressedBytecode function. In this case, the calldata will have the following format:
4 bytes : `publishCompressedBytecode` selector 32 bytes : offset for `_bytecode` parameter = V 32 bytes : offset for `_rawCompressedData` parameter = V + 32 + rounded_len(_bytecode) (V - 64) bytes : any bytes that will be ignored in the `publishCompressedBytecode` function 32 bytes : length of `_bytecode` parameter = len(_bytecode) rounded_len(_bytecode) bytes : `_bytecode` parameter = _bytecode 32 bytes : length of `_rawCompressedData` parameter = len(_rawCompressedData) rounded_len(_rawCompressedData) bytes : `_rawCompressedData` parameter = _rawCompressedData
  1. There are used unsafe "raw" ariptmetic operations in the calculations of the offsets, while the safe analogs should be used. This leads to the possibility of passing absolutely wrong data so the call of the Compressor::publishCompressedBytecode function will revert. As this bug does not provide any strong attack vector I will present only the idea of how to achieve this, not the whole calldata: the offset of the _bytecode parameter should be equal (2**256) - 64 (so the originalBytecodeOffset will pass the checkOffset check), rest of the parameters is easy to determine over this. The same happens in the bootloader::validateBytes function, as in the bootloader::lengthRoundedByWords function the overflow happens, leading to the same outcome.

Impact

The ability of the operator to pass up to ~COMPRESSED_BYTECODES_SLOTS * 32 = ~1048576 additional bytes into the call of the Compressor::publishCompressedBytecode function. Such possibility of spending part of the transaction gas limit makes the operator able to actually decrease the L2 transaction gas limit, which leads to the possibility of manipulation of the amount of gas used for the transaction execution.

The ability of the operator to pass encoding-related checks of the bootloader::sendCompressedBytecode function, which leads to the possibility of forcing the Compressor::publishCompressedBytecode function call to revert.

  1. You can add the following check into the bootloader::sendCompressedBytecode function:
if iszero(eq(add(afterSelectorPtr, 64), originalBytecodeOffset)) {
    assertionError("Compression calldata incorrect")
}
  1. You can use safeAdd instead of add in the corresponding calculations in the bootloader::sendCompressedBytecode function.

Assessed type

Other

#0 - c4-pre-sort

2023-11-01T08:02:31Z

bytes032 marked the issue as low quality report

#1 - c4-judge

2023-11-26T09:05:53Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#2 - Barichek

2023-12-04T11:08:24Z

@GalloDaSballo @vladbochok

I believe that the described finding is a medium-severity issue.

There is another issue which leads to possibility of [operator to burn excess gas as a strategy to maximize profits] and which is fairly evaluated as medium-severity. Current issue (especially, point "1)" of the current issue) describes the same consequence, so it should be evaluated as the same severity.

Also, one of the consequences of this vulnerability is the possibility to [break trust expectations by purposefully making some txs to fail via OOG], while similar situation was judged as medium-severity. Moreover, this vulnerability opens for the operator ability to burn only part of the transaction gas, making it possible to manipulate on the amount of gas used for transaction execution.

#3 - c4-judge

2023-12-07T10:22:09Z

This previously downgraded issue has been upgraded by GalloDaSballo

#4 - c4-judge

2023-12-07T10:22:09Z

This previously downgraded issue has been upgraded by GalloDaSballo

#5 - GalloDaSballo

2023-12-07T10:23:18Z

The Warden has demonstrated how unsafe arithmetic could be used by the operator to pass incorrect data that "checks" down to the expected values

This could be used to pass incorrect compressed data as well as abuse gas costs as means to cause end users to be overcharged

Because of this, I agree with Medium Severity

#6 - c4-judge

2023-12-07T10:23:23Z

GalloDaSballo marked the issue as selected for report

#7 - c4-sponsor

2024-02-26T14:49:51Z

vladbochok (sponsor) confirmed

Findings Information

🌟 Selected for report: Audittens

Labels

bug
2 (Med Risk)
selected for report
sponsor confirmed
sufficient quality report
M-08

Awards

32626.9346 USDC - $32,626.93

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1244-L1245 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1255-L1259 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1365 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1562-L1579 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/Compressor.sol#L54-L82

Vulnerability details

Description

In the bootloader::sendCompressedBytecode function the nearCallPanic() is used for the case of unsuccessfull call of the Compressor::publishCompressedBytecode function. In practice, such situation could happen because of invalid data provided by the operator, as any of the internal checks of this function could be ignored by the operator to make the call to revert: decoding of the _rawCompressedData (link), the checks on dictionary length (link1, link2), the check on the encodedData length (link), etc.

Such possibility of forcing the overall bootloader::ZKSYNC_NEAR_CALL_markFactoryDepsL2 to near call revert, makes the operator able to burn all the gas of the L2 transaction if there is at least one bytecode to be published at the start of transaction processing.

Impact

The ability of the operator to burn all the gas of the L2 transaction in the call of the bootloader::ZKSYNC_NEAR_CALL_markFactoryDepsL2 function, if at least one bytecode is to be published.

Use revertWithReason instead of nearCallPanic for the cases of unsuccessful calls of the Compressor::publishCompressedBytecode function. The cases of out of gas error on such calls operator can process as standard bytecode publications through the bootloader::markFactoryDepsForTx functionality.

Assessed type

Other

#0 - c4-pre-sort

2023-11-02T14:02:58Z

141345 marked the issue as sufficient quality report

#1 - miladpiri

2023-11-06T17:54:04Z

This has medium severity because it gives the operator the ability to forcefully fail the transaction.

#2 - c4-sponsor

2023-11-06T17:54:12Z

miladpiri (sponsor) confirmed

#3 - c4-judge

2023-11-28T12:51:15Z

GalloDaSballo marked the issue as selected for report

#4 - GalloDaSballo

2023-11-28T12:51:53Z

The finding is similar to #71, however in this case it shows how the Operator can break trust expectations by purposefully making tx fails via OOG

Findings Information

🌟 Selected for report: Audittens

Labels

bug
2 (Med Risk)
disagree with severity
selected for report
sponsor confirmed
sufficient quality report
M-09

Awards

32626.9346 USDC - $32,626.93

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L318 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L305-L306 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L323 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1656-L1659 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L2321 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L2328-L2333 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/DefaultAccount.sol#L122-L129

Vulnerability details

Description

ETH on L2 stored on EOAs is inaccessible through L1->L2 transactions, as for such transactions msg.value is generated only "from L1 ETH", not from the active balance of the tx.origin on L2. This is so because of the logic in the Malibox::_requestL2Transaction function, which enforces msg.value of the L1->L2 transaction to be only part of ETH to be minted inside of transaction processing on L2. Bootloader maintains this system invariant as well.

DefaultAccount incorrectly assumes that L1->L2 transactions initiated by EOA cover the needed functionality of the executeTransactionFromOutside function, so it is empty.

Users will be unable to access their ETH on L2 in the case of a malicious operator that ignores L2 transactions (so if it processes only L1->L2 transactions). Also, and importantly, this violates the security invariant that enforces the ability of the users to access L2 through L1->L2 transactions, and, therefore, withdraw funds from the rollup before the upgrade in case of a scheduled malicious upgrade.

Impact

No access to ETH on L2 that is controlled by EOAs in case of malicious operator.

Violation of the "guarantee of withdraw of funds before malicious upgrade execution" security invariant.

Add a possibility to create L1->L2 transactions that can use ETH from the L2 balance as part of the msg.value. As an alternative, you can implement the DefaultAccount::executeTransactionFromOutside function so it will contain the expected behavior, as stated in the comment above its declaration.

Assessed type

Other

#0 - c4-pre-sort

2023-10-31T09:39:17Z

bytes032 marked the issue as sufficient quality report

#1 - bytes032

2023-11-01T16:08:32Z

malicious operator ignores L2 transaction

#2 - miladpiri

2023-11-06T11:28:45Z

Warden states that if an EOA has some fund on L2, he can not withdraw it by requesting a tx from L1. The only way is to make a tx on L2. But, if the oprator is malicious and only process L1toL2 txs, not L2 txs, then user’s fund will be trapped.

The scenario requires a malicious opearot ignoring L2 txs. The main issue is that it is not possible to withdraw the fund of an account on L2 through L1 -> L2 transaction. This is duplicate of https://github.com/code-423n4/2023-10-zksync-findings/issues/827

Medium is fair.

#3 - c4-sponsor

2023-11-06T11:28:52Z

miladpiri marked the issue as disagree with severity

#4 - c4-sponsor

2023-11-06T11:49:16Z

miladpiri (sponsor) confirmed

#5 - c4-judge

2023-11-28T12:50:11Z

GalloDaSballo changed the severity to 2 (Med Risk)

#6 - c4-judge

2023-11-28T14:47:16Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#7 - GalloDaSballo

2023-11-29T07:21:56Z

It seems like I may have misunderstood this findings and similar findings related to L1->L2 execution, please send me an explanation as to exactly what function is missing that prevents this from happening and I'll re-doup this category of issues

#8 - Barichek

2023-12-04T11:09:05Z

@GalloDaSballo @vladbochok

The main point of the described finding is about the violation of one of the main zkSync rollup invariants. Namely, the invariant mentioned in the finding is the "ability of the users to access L2 through L1->L2 transactions, and, therefore, withdraw funds from the rollup before the upgrade in case of a scheduled malicious upgrade".

This invariant should be maintained in the system design, as L1->L2 txs are currently the only one legal way to enforce the ability of the user to access his funds in case of the malicious operator. Therefore, it is a critical point that is the only difference between the current zkSync design and the sidechain with transitions proved by zkp.

Otherwise, it wouldn't make sense to publish state diffs using on-chain calldata: such data will be required to reconstruct the rollup state and force/process transactions in case of the malicious operator, but in case the operator can ignore L2 transactions and there is no access to ETH on L2 through L1->L2 txs there is no protection against the malicious operator. This confirms that the described vulnerability is no less serious than the "ability of the operator to not publish some of the rollup state diffs" or the "ability of the operator to ignore L1->L2 transactions (in the long-term system design with escape hatch)" (both of which are high-severity findings).

While the finding #48 and its duplicates are about the incorrect logic of the refundRecipient aliasing process, this finding is about violation of one of main system invariant.

Along with the facts that most of TVL of zkSync is native ETH (link1, link2), and custom wallets are not widely adopted on zkSync, most of the value locked in the zkSync fit the described situation.

The described above confirms the fact that the described vulnerability is a high-severity issue.

#9 - vladbochok

2023-12-04T13:04:06Z

Hey @GalloDaSballo @Barichek,

I agree that #48 is a different finding, that describes another issue. Also, I can confirm that this is a valid submission. That being said by @Barichek, the problem is censorship resistance protection, so user can't escape funds from the rollup using L1 -> L2 communication.

#10 - 0xunforgiven

2023-12-04T13:31:10Z

Hey @GalloDaSballo, @vladbochok. Thanks for reviewing my comment.

In following the above comment from @vladbochok regarding that #803 is different issue:

I just want to add that Issue #457 explain the both impacts of the issue root cause for EOA and L1-contracts:

there could be a lot of scenarios. the fact that L1-contracts can't spend and send its own balance in L2 (the balance in aliased address) is a critical bug. This bug would happen for EOA addresses to but the impact is that EOA addresses can't handle their funds from L1 but they can still create L2 transaction directly. so the impact would be that the censorship mechanism in zkSync L1 contracts (queue of L1->L2 transaction) won't work for all transactions. and users for handling their L2 balance need to create L2 transaction directly which can be censored.

In case the issues #803 (EOA impact) and #827 (L1-contracts impact) get validated as separate issues, I believe #457 should be duplicate of both of them.

I also put more comments about this issue's root cause and impacts(which are explained in #457) in comment1 and comment2

#11 - c4-judge

2023-12-10T18:18:54Z

This previously downgraded issue has been upgraded by GalloDaSballo

#12 - c4-judge

2023-12-10T18:18:54Z

This previously downgraded issue has been upgraded by GalloDaSballo

#13 - c4-judge

2023-12-10T18:19:06Z

GalloDaSballo marked the issue as selected for report

#14 - GalloDaSballo

2023-12-10T18:20:52Z

The Warden has shown how due to a lack of implementation of `executeTransactionFromOutside`Β a malicious operator could prevent any withdrawal by denying L2 txs

Because of the specific scoping this is valid, because of the reliance on a malicious operator, Medium Severity seems the most appropriate

#15 - Audittens

2023-12-10T20:06:42Z

Hi @GalloDaSballo.

With all due respect, I believe that this issue should be classified as high-severity vulnerability.

Main possible impact of most of the already approved high-severity vulnerabilities is the possibility of the operator (and only of him) to maliciously steal funds:

  • issue #1133 impact is A malicious validator could generate and submit a proof with incorrect behavior of the div instruction.
  • issue #761 impact is Attacker can manipulate the sorted queue in log sorter, constraints are not strong enough and reverted l1 logs and events can still be emitted.
  • issue #702 impact is Attempting to read a word that spans across the pointer bound start+offset should return zero bytes for the addresses. When offset >= length, the result should be completely zeros, so we can skip the read. However, in current implementation, this case is not handled properly so that attacker can forge arbitary read result.
  • issue #697 impact is A malicious validator could generate and submit a proof with incorrect behavior of the shr instruction.
  • issue #679 impact is Attacker can forge arbitary result for opcode and and or; as for xor, an overflowed UInt8 will stay in the circuit, which can lead to unexpected behavior.

While the above-mentioned reports lead to some sort of unexpected behaviours and incorrect results of execution of some of VM functionality, the current report (#803) gives the operator ability to completely freeze the ETH value stored on zkSync (with absolutely no way to prove on L1 that the operator is ignoring l2 txs).

I believe, that such possibility leads to practically the same consequences, as the ability of the operator to steal the funds -- it is actually the analogue of the game where one party (in our case it is an operator) have an unique right to completely freeze funds of the other party (in our case it is the users), along with the unique right to unfreeze the funds at any moment in the future. The optimal result of this game have (asymptotically) the same consequences as the result of the situation where the first party can easily steal the funds of the other.

Along with the mentioned facts that most of TVL of zkSync is native ETH (link1, link2) and custom wallets are not widely adopted on zkSync (so most of the value locked in the zkSync fit the described situation), I believe that this issue is a high-severity vulnerability.

#16 - xuwinnie

2023-12-10T23:49:52Z

@Audittens This issue relies on the assumption that operator is centralized, but in the foreseeable future, zksync operator will go decentralized. Before the decentralization, this issue slightly increases the trust assumption. It's worth noting that currently priority queue only ensures censorship resilience( operator can still completely ignore the queue, but can't select from the queue) So even if this issue is fixed, operator can still freeze everyone's fund.This issue only lets the operator to freeze certain user's fund without disrupting the priority queue. After the decentralization, the highs you mentioned will be exploitable, while this issue will no longer exist.

Findings Information

🌟 Selected for report: erebus

Also found by: AkshaySrivastav, Audittens, HE1M, dontonka, twcctop

Labels

bug
2 (Med Risk)
low quality report
satisfactory
duplicate-260

Awards

3293.3126 USDC - $3,293.31

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/governance/Governance.sol#L70-L76 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/governance/Governance.sol#L154-L158 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/governance/Governance.sol#L186

Vulnerability details

Description

According to the implementation of the Governance::cancel function, the security council can cancel any operation that was scheduled by the governance owner.

While this feature creates the right for the security council to cancel malicious operations scheduled by the owner, it does not create additional protection for users of the rollup -- protection against a scheduled malicious operation (in practice, rather, a malicious upgrade) comes from the minimum delay for such operations, and only such protection can be considered as guaranteed by the smart contract and sufficient (in practice it is sufficient due to invariant that a user will be able to enforce the execution of an L1->L2 operation using L1 priority queue while the upgrade has not yet been completed).

At the same time, this feature creates unnecessary security assumptions about the security council.

Attack scenario

As a demonstrative example of the problem (with a realistic application of the governance functionality -- rollup upgrades management), one can consider the following scenario:

  1. There is a bug in the rollup system where it is possible to corrupt the rollup state invariants/system contracts invariants/some DeFi project on zkSync with a very great TVL/...
  2. The bug allows the attacker to break the system only over a relatively long period (for example, 3 times the minimum delay for the governance operation)
  3. After some entity initiated the attack no other entity can "take the attacker's place" (for example, the attacker's address has been stored at the storage, and, due to some specifics of the vulnerability, no other entity can override it -- the only way to stop the attacker is to conduct the upgrade)
  4. The attacker can be a smart contract entity, with the following code implemented: I) Smart contract automatically performs all necessary actions to break the system -- anyone can call necessary functions to process the attack and finalize it at the end of the scenario II) When the attack is over, the smart contract automatically splits all the profit from the attack between hardcoded security council accounts and attacker account using the hardcoded ratio

In such a situation, the owner will schedule an upgrade to fix the vulnerability and protect the system. But there is a very powerful economic incentive to cancel such a scheduled upgrade (and in general all owner's attempts to upgrade the rollup).

Also, the system does not have any (at least publicly mentioned) incentive/enforcement that the security council will execute an instant upgrade in case of the analogical scenario but with a smaller required time delay for the attacker.

Impact

A very powerful economic incentive for the security council to cancel upgrades proposed by the owner, with almost zero practical benefit to the system from such a possibility.

Lack of system incentive/enforcement that the security council will execute instant upgrades in case of need.

Allow only the governance owner to cancel scheduled governance operations.

Create additional incentives for the security council to execute instant upgrades if necessary.

Assessed type

Access Control

#0 - c4-pre-sort

2023-10-31T09:45:57Z

bytes032 marked the issue as low quality report

#1 - miladpiri

2023-11-08T14:24:53Z

#2 - c4-judge

2023-11-23T16:47:28Z

GalloDaSballo marked the issue as duplicate of #260

#3 - c4-judge

2023-11-28T15:52:47Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: HE1M

Also found by: Audittens

Labels

bug
2 (Med Risk)
low quality report
satisfactory
duplicate-168

Awards

25097.642 USDC - $25,097.64

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/DefaultAccount.sol#L222-L227 https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/DefaultAccount.sol#L45

Vulnerability details

Description

The DefaultAccount contract's fallback function is designed to prevent any calls from the bootloader using an assertion check against the bootloader's formal address. However, the fallback function doesn't consider the scenario where another account, when invoked by the bootloader, uses a delegatecall to call the DefaultAccount contract's fallback. In such a scenario, the assertion check will fail, causing the contract to panic instead of behaving neutrally.

Attack scenario

Someone can create a contract that, when invoked by the bootloader, utilizes a delegatecall to the DefaultAccount's fallback function. This will trigger the assert statement, causing the DefaultAccount to panic, which is an unintended behavior.

Impact

The vulnerability may lead to unexpected behavior in the contract operation, potentially causing disruptions or, in worst-case scenarios, loss of funds or other unintended consequences.

Add ignoreInDelegateCall modifier to DefaultAccount's fallback function.

Assessed type

call/delegatecall

#0 - c4-pre-sort

2023-10-31T07:06:58Z

bytes032 marked the issue as low quality report

#1 - miladpiri

2023-11-08T14:17:47Z

#2 - c4-judge

2023-11-21T13:04:42Z

GalloDaSballo marked the issue as duplicate of #168

#3 - c4-judge

2023-11-28T12:48:25Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: HE1M

Also found by: Audittens, OffsideLabs, zkrunner

Labels

bug
2 (Med Risk)
disagree with severity
low quality report
satisfactory
duplicate-71

Awards

6776.3633 USDC - $6,776.36

External Links

Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1244-L1245 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1255-L1259 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1365 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/scripts/process.ts#L123 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/Compressor.sol#L54-L82 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/Compressor.sol#L62

Vulnerability details

Description

There is no enforcement in the system that the compressed bytecode to be published was compressed optimally. Moreover, there is no enforcement that the compressed data to be published is not longer than the original bytecode. The only upper bounds for the unused data of "_rawCompressedData" (specifically, dictionary) are 524288 bytes from the Compressor::publishCompressedBytecode function and ~32768 slots == ~1048576 bytes from the bootloader memory restriction.

This gives the operator the ability to spend a great amount of gas on the emulation of the publication of a fake "compressed bytecode" (with length up to ~524288 bytes) in any case, even when the real bytecode is relatively short. This can cost for the transaction initiator the same as publication of ~524288 non-zero bytes in L1 calldata (which is ~524288 * 16 = ~8'388'608 gas on L1). Please note, that the operator actually will not publish this data on L1 in most of such cases, as in practice users will not initiate transactions with so a great L2 gas limit. And even for the cases when the gas limit of the transaction enforces publication of such data on L1, this attack is profitable for the operator.

Impact

Ability of the operator to perform publication of up to ~524288 bytes of the "compressed bytecode", for each of the bytecodes that are to be published at the transaction preparation.

In most of the practical cases, the gas for such operation will be burned (the cost of it will be paid by the user and the "body" of the L2 transaction will be not executed), while the real publication of the bytecode will be skipped due to revertion of the bootloader::ZKSYNC_NEAR_CALL_markFactoryDepsL2 function.

Also, and importantly, such possibility of spending part of the transaction gas limit makes the operator able to actually decrease the L2 transaction gas limit, which leads to the possibility of manipulation of the amount of gas used for the transaction execution.

Enforce that the length of the compressed data in the bytecode publication is not greater than the original bytecode length. Namely, you can add the following check to the Compressor::publishCompressedBytecode function:

require(_rawCompressedData.length < _bytecode.length, "Compressed data should be shorter than the original bytecode");

To make this check more accurate, there should be a counting of non-zero bytes in both arrays and the corresponding weighted check.

Assessed type

Other

#0 - c4-pre-sort

2023-11-01T08:01:43Z

bytes032 marked the issue as low quality report

#1 - miladpiri

2023-11-08T14:22:26Z

Low, as it is not clearly mentioning that how the operator can consume lots of gas, just mentioning that make a long compressed data.

#2 - c4-sponsor

2023-11-08T14:22:31Z

miladpiri marked the issue as disagree with severity

#3 - c4-judge

2023-11-16T13:50:53Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#4 - Barichek

2023-12-04T11:08:29Z

@GalloDaSballo @vladbochok

The described finding is an absolute duplicate of the finding #71.

The description in the "Description" section of the report states the possibility of the elongation of the dictionary parameter with unnecessary data up to 524288 bytes.

#5 - c4-judge

2023-12-07T18:58:28Z

This previously downgraded issue has been upgraded by GalloDaSballo

#6 - c4-judge

2023-12-07T18:58:28Z

This previously downgraded issue has been upgraded by GalloDaSballo

#7 - c4-judge

2023-12-07T18:58:49Z

GalloDaSballo marked the issue as duplicate of #71

#8 - c4-judge

2023-12-07T18:58:54Z

GalloDaSballo marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter