zkSync v2 contest - HE1M's results

Rely on math, not validators.

General Information

Platform: Code4rena

Start Date: 28/10/2022

Pot Size: $165,500 USDC

Total HM: 2

Participants: 24

Period: 12 days

Judge: GalloDaSballo

Total Solo HM: 1

Id: 177

League: ETH

zkSync

Findings Distribution

Researcher Performance

Rank: 2/24

Findings: 2

Award: $34,676.26

QA:
grade-a

🌟 Selected for report: 2

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: HE1M

Also found by: codehacker

Labels

bug
2 (Med Risk)
primary issue
sponsor confirmed
edited-by-warden
selected for report
M-01

Awards

31943.2548 USDC - $31,943.25

External Links

Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/DiamondCut.sol#L46 https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/libraries/Diamond.sol#L277

Vulnerability details

Impact

When the governor proposes a diamondCut, governor must wait for upgradeNoticePeriod to be passed, or security council members have to approve the proposal to bypass the notice period, so that the governor can execute the proposal.

require(approvedBySecurityCouncil || upgradeNoticePeriodPassed, "a6"); // notice period should expire require(approvedBySecurityCouncil || !diamondStorage.isFrozen, "f3");

If the governor's key is leaked and noticed by zkSync, the attacker must wait for the notice period to execute the already proposed diamondCut with the malicious _calldata based on the note below from zkSync, or to propose a new malicious diamondCut. For, both cases, the attacker loses time.

NOTE: proposeDiamondCut - commits data associated with an upgrade but does not execute it. While the upgrade is associated with facetCuts and (address _initAddress, bytes _calldata) the upgrade will be committed to the facetCuts and _initAddress. This is done on purpose, to leave some freedom to the governor to change calldata for the upgrade between proposing and executing it.

Since, there is a notice period (as zkSync noticed the key leakage, security council member will not approve the proposal, so bypassing the notice period is not possible), there is enough time for zkSync to apply security measures (pausing any deposit/withdraw, reporting in media to not execute any transaction in zkSync, and so on).

But, the attacker can be smarter, just before the proposal be executed by the governor (i.e. the notice period is passed or security council members approved it), the attacker executes the proposal earlier than governor with the malicious _calldata. In other words, the attacker front runs the governor.

Therefore, if zkSync notices the governor's key leakage beforehand, there is enough time to protect the project. But, if zkSync does not notice the governor's key leakage, the attacker can change the _calldata into a malicious one in the last moment so that it is not possible to protect the project.

Proof of Concept

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/libraries/Diamond.sol#L277 https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/DiamondCut.sol#L46

Tools Used

_calldata should be included in the proposed diamondCut: https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/DiamondCut.sol#L27

Or, at least one of the security council members should approve the _calldata during execution of the proposal.

#0 - c4-sponsor

2022-11-22T21:36:48Z

miladpiri marked the issue as sponsor confirmed

#1 - miladpiri

2022-11-22T21:39:06Z

It is a valid issue, and the fix is going to be implemented, so we confirm the issue as medium! Thanks.

#2 - GalloDaSballo

2022-12-02T21:12:39Z

In contrast to other reports, this shows how a malicious proposal could be injected, bypassing the timelock protection, for this reason (after consulting with a second Judge), I agree with marking it a distinct finding and agree with Medium Severity

#3 - c4-judge

2022-12-02T21:12:45Z

GalloDaSballo marked the issue as selected for report

#4 - c4-judge

2022-12-02T21:36:20Z

GalloDaSballo marked the issue as primary issue

Findings Information

🌟 Selected for report: HE1M

Also found by: 0xSmartContract, Rolezn, Tomo, brgltd, cccz, chaduke, ctf_sec, datapunk, jayjonah8, ladboy233, pashov, rbserver

Labels

bug
QA (Quality Assurance)
edited-by-warden
grade-a
selected for report
Q-02

Awards

2733.0139 USDC - $2,733.01

External Links

No. 1

The critical parameters in initialize(...) are not set safely:

  • s.governor should be set to msg.sender, because a wrong governor address will result in loss of access to all other parts, and later changing the governor to the correct address.
  • _l2BootloaderBytecodeHash should be validated like L2ContractHelper.validateBytecodeHash(_l2BootloaderBytecodeHash) as in GovernanceFacet
  • _l2DefaultAccountBytecodeHash should be validated like L2ContractHelper.validateBytecodeHash(_l2DefaultAccountBytecodeHash) as in GovernanceFacet

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/DiamondInit.sol#L39 https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/DiamondInit.sol#L58 https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/DiamondInit.sol#L59

No. 2

Better to have also approveEmergencyDiamondCutAsSecurityCouncilMemberBySignature, in case the security council members do not have access to ethereum blockchain or in case it is needed to approve just in one transaction by batch of signatures (to bypass the notice period).

No. 3

Better to have config facet, in case some update is needed in the config.sol. Therefore, it is not necessary to redeploy the facets that imported config (like Executor and Mailbox)

No. 4

L2_LOG_BYTES is not correct, it should be L2_TO_L1_LOG_SERIALIZE_SIZE https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/zksync/Config.sol#L19

No. 5

It is not needed to have modifier senderCanCallFunction for the function deposit in both L1ERC20Bridge and L1ETHBridge, because they call the function requestL2Transaction in the MailBox that has already such modifier. https://github.com/code-423n4/2022-10-zksync/blob/358f38736351a8a27e325dfcb665eeba5ec02bd5/ethereum/contracts/bridge/L1EthBridge.sol#L92 https://github.com/code-423n4/2022-10-zksync/blob/358f38736351a8a27e325dfcb665eeba5ec02bd5/ethereum/contracts/zksync/facets/Mailbox.sol#L112

No. 6

For each deposit of an ERC20 token, the information of the token is read and packed and sent to L2. Even if this token is used before for deposit, again this information is sent to L2, which is waste of gas. https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L164-L169 https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L155

No. 7

When a block is committed, its hash will be stored in storedBlockHashes: https://github.com/code-423n4/2022-10-zksync/blob/358f38736351a8a27e325dfcb665eeba5ec02bd5/ethereum/contracts/zksync/facets/Executor.sol#L164 If this block is reverted, it is not removed from storedBlockHashes: https://github.com/code-423n4/2022-10-zksync/blob/358f38736351a8a27e325dfcb665eeba5ec02bd5/ethereum/contracts/zksync/facets/Executor.sol#L336 The vulnerability is that in the GettersFacet, the function storedBlockHash(...) will return the hash of a reverted block if this block number is given as its parameter, while it should return 0. https://github.com/code-423n4/2022-10-zksync/blob/358f38736351a8a27e325dfcb665eeba5ec02bd5/ethereum/contracts/zksync/facets/Getters.sol#L86

#0 - GalloDaSballo

2022-11-24T01:29:52Z

The critical parameters in initialize(...) are not set safely:

Logically equivalent to address(0) check, L

approveEmergencyDiamondCutAsSecurityCouncilMemberBySignature

Not sure what this means, but if it means one caller get's to approve emerengency cuts, this is a glaring security risk

Better to have config facet, in case some update is needed in the config.sol

Not convinced by this one either, ultimately it's called config but it's just a bunch of contact / base contract

L2_LOG_BYTES is not correct, it should be L2_TO_L1_LOG_SERIALIZE_SIZE

NC

It is not needed to have modifier senderCanCallFunction for the function deposit in both L1ERC20Bridge and L1ETHBridge, because they call the function requestL2Transaction in the MailBox that has already such modifier.

Not convinced in lack of detail, if you call contract X and contract X calls contract Y, then the check is necessary on both contracts

For each deposit of an ERC20 token, the information

Unclear what you'd do and were the savings would be

When a block is committed, its hash will be stored in storedBlockHashes:

See #204 L

#1 - GalloDaSballo

2022-12-02T22:20:14Z

2L 1NC

#2 - GalloDaSballo

2022-12-03T19:08:05Z

Report was pretty good, but the downgraded findings add a lot of points to this QA.

While the Warden has sent a few false positives, I think the value they offered for this contest warrants them winning the best QA report

#3 - c4-judge

2022-12-03T19:08:11Z

GalloDaSballo marked the issue as selected for report

#4 - c4-judge

2022-12-03T20:59:16Z

GalloDaSballo marked the issue as grade-a

#5 - miladpiri

2022-12-03T22:16:22Z

Thanks @GalloDaSballo for your comment, that now we are looking at this report more seriously. IMO, No.2 “approveEmergencyDiamondCutAsSecurityCouncilMemberBySignature“ can be a security issue. This is what we are now implementing to make security council members be able to approve the proposal by signature. The security issue is that in case the security council members do not have access to approve on-chain, the governor must wait for their approval to bypass the notice period. In case the proposal should be executed instantly ( in case of a critical bug in our protocol), this delay can be dangerous to our protocol. By having approval by signature, the security members can approve a proposal off-chain, and the governor can execute the instant upgrade on behalf of them by using their signatures, so all-time access to the chain is not necessary for security council members. Moreover, in case a critical proposal should be executed silently, it is necessary to be able to approve by signature. Because, the governor, in one batch transaction, proposes the proposal, and approves the upgrade on behalf of the security council members by using their signatures, and then executes the proposal. Since all the above actions are done in one transaction, there is no security risk of vulnerability leak. But, in the current structure, when the governor proposes the proposal, should wait for security members’ approval, this delay can be transparent to a malicious user to investigate the governor’s proposal, and gets a clue what the vulnerability is and then exploits the protocol. This report worths to be upgraded in terms of severity! Thanks.

#6 - miladpiri

2022-12-06T22:46:47Z

@GalloDaSballo I would like to know your idea about my comment above. Thanks!

#7 - GalloDaSballo

2022-12-06T23:57:12Z

@GalloDaSballo I would like to know your idea about my comment above. Thanks!

Thank you for your insight, I have sent the contest to triage, took note of your feedback and am sharing with other Judges and Wardens

#8 - GalloDaSballo

2022-12-08T23:52:49Z

Similarly to #48 I have shared this finding with 2 Judges and a Top warden and we all agree that this is effectively the same finding, with similar impact.

As such Low Severity is the most appropriate.

#9 - miladpiri

2022-12-09T08:20:23Z

Thanks for the follow-up!

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