Jul 13, 2023
zkSync Security Practices
zkSync serves as a Layer 2 scaling solution for Ethereum, leveraging zero-knowledge rollups (zk-rollups) to tackle the scalability issues inherent to the Ethereum blockchain. In the zkSync approach, transactions are executed off-chain and subsequently consolidated into a single proof that is submitted to the Ethereum mainnet for validation. This method significantly boosts transaction throughput, reduces costs, and bolsters privacy, all while preserving the security and decentralization of the Ethereum network. The zkSync Era marks the first implementation of native account abstraction on an EVM-compatible chain.
0x00:Best Practices for zkSync Security
When embarking on zkSync Era development, it is essential to take into account the following recommendations. These guidelines are crucial for optimizing code, ensuring security, and aligning with the distinct characteristics of zkSync Era. By adhering to these best practices, you can enhance code performance, fortify security measures, and embrace the unique aspects of zkSync Era development.
1.Use call instead of transfer and send on an address payable
The recommendation of using call over .send or .transfer pertains to the handling of external contract calls in Solidity.
In Solidity, there are three ways to send Ether or invoke a function on another contract: send, transfer, and call.
send and transfer are simplified methods for sending Ether to another contract, but they have some limitations. They only provide a limited amount of gas (2,300 gas) for the execution of the receiving contract’s fallback function, and they automatically revert the transaction if the execution fails or the receiving contract runs out of gas.
Instead of:
On the other hand, call is a lower-level function that provides more control and flexibility. It allows specifying the amount of gas to be sent and enables error handling. With call, you can handle the return values and check for success or failure of the external contract call. If the call fails, it returns false instead of reverting the entire transaction.
Use instead:
while it is true that using call provides more flexibility and control, developers need to be mindful of the potential reentrancy vulnerabilities associated with it compared to send or transfer.
When using call, the receiving contract’s fallback function can be reentered before the completion of the current call, potentially leading to unexpected behaviors and security vulnerabilities. To mitigate this risk, it is important to follow best practices such as implementing the checks-effects-interactions pattern and incorporating reentrancy guard protection.
The checks-effects-interactions pattern involves separating the different stages of contract interactions, ensuring that critical state changes are made before any external calls are made. This helps prevent reentrancy attacks by minimizing the window of opportunity for malicious contracts to reenter the current contract during an external call.
In addition, using reentrancy guard protection can further enhance the security of your contracts. Reentrancy guards are mechanisms that restrict multiple entry points into sensitive functions, preventing reentrant calls from external contracts and ensuring that the execution flow remains controlled and predictable.
By adhering to these best practices and incorporating reentrancy guard protection, you can enhance the robustness and security of your smart contracts on the zkEVM, even in the face of unexpected conditions and potential reentrancy vulnerabilities.
2.Employ the proxy pattern during the initial phase of the protocol’s development.
During the early stages of the zkSync Era protocol, it is recommended to utilize the Proxy pattern for your smart contracts. The zkSync Era is built on a zk-friendly Virtual Machine (VM) and offers a compiler that can convert standard Solidity code into zkEVM bytecode.
While zkSync Era have conducted extensive testing to ensure compatibility with the Ethereum Virtual Machine (EVM), it is still possible for issues to arise.
To apply these bug fixes, it may be necessary to upgrade your smart contracts. As a precautionary measure, we suggest implementing the Proxy pattern for a few months following your initial deployment on zkSync Era, even if you have plans to eventually migrate to an immutable contract.
Using the Proxy pattern allows for greater flexibility in making updates and fixes to your contract logic without requiring a full contract redeployment. It acts as a layer of abstraction that enables seamless upgrades and bug fixes while preserving the integrity and continuity of your application.
By adopting the Proxy pattern during the early stages of zkSync Era, you can ensure that any necessary upgrades or bug fixes can be efficiently implemented, providing a smoother transition and increased resilience to potential issues in the protocol.
3.Avoid relying on Ethereum’s gas logic
zkSync Era has its own gas logic, which differs from Ethereum. This is primarily due to the state diff-based fee model and the unique computational trade-offs of zkEVM. Gas prices and opcode costs may vary compared to Ethereum, and relying solely on gas calculations can be insufficient.
One specific aspect to consider is the gasPerPubdataByte constant included in every transaction. While the operator currently controls this value, users sign an upper bound for it. However, the operator can still choose any value within that range, influenced by the L1 gas price. Failing to account for gasPerPubdata can lead to transaction failures and unnecessarily high gas consumption.
Let’s take the example of Gnosis Safe’s execTransaction method. As the screenshot below While the contract ensures the proper management of gasleft(), it fails to account for the gasPerPubdata parameter, which is specific to zkSync Era and not present in Ethereum. This vulnerability allows a malicious user to exploit the wallet by calling it with a high gasPerPubdata value, causing the transaction to fail and resulting in excessive gas consumption.
In other words, because the gasPerPubdata parameter is not considered in the contract’s logic, an attacker can manipulate it to intentionally cause transaction failures and inflate gas usage beyond what is necessary.
4.Opt for Native Account Abstraction Instead of ecrecover for Verification
The zkSync Era is the first EVM-compatible chain to implement native account abstraction. As the screenshot below,accounts in the zkSync Era can initiate transactions like Externally Owned Accounts (EOAs), but they can also implement any logic within them, like smart contracts. This feature is known as “Account Abstraction” (AA). Therefore, when performing signature verification, one should not rely on the fact that the account has an ECDSA private key, as the account may be managed by multi-signatures and use other signature schemes. It is recommended to use the native account abstraction support of the zkSync Era to replace the ecrecover function for signature verification.
0x01:The First Security Incident on zkSync Era - The Merlin Hack
Merlin, a decentralized exchange built on zkSync Era, experienced a security breach on April 26, 2023[2]. The funds held in the USDC-WETH liquidity pool were completely withdrawn by an attacker, resulting in a total profit of approximately $1.8 million. Now let’s delve deeper into this incident and gain a comprehensive understanding.
The following code snippet shows that the function initializes the contract by setting the token addresses, approving spending allowances, and setting precision multipliers.
The protocol approves an unlimited amount of tokens from token0 and token1 to be spent by the address specified by IMerlinSwapFactory(factory).feeTo()(lines 87-88). This allows the specified address to spend tokens on behalf of this contract.
The core issue lies in the fact that the feeTo address is controlled by a hacker. After gaining maximum authorization through the type(uint256).max approval, the hacker proceeded to exploit this privilege by directly calling the transferFrom function to transfer the funds.
This is the first and currently the only security incident on zkSync Era,it highlights the importance of implementing robust security measures, conducting thorough audits, and employing strict access controls to prevent unauthorized access and protect user funds from being exploited by malicious actors.
Reference:
The merlin hack incident : https://rekt.news/merlin-dex-rekt/
About ScaleBit
ScaleBit is a blockchain security team that provides security solutions for Mass Adoption of Web3. With expertise in scaling technologies like blockchain interoperability and zero-knowledge proofs, we provide meticulous and cutting-edge security audits for blockchain applications. The team comprises security professionals with extensive experience in both academia and enterprise. Our mission is to provide security solutions for Web3 Mass Adoption and make security accessible for all.
Website: https://www.scalebit.xyz/
Twitter: https://twitter.com/scalebit_
ScaleBit Research Group