Open c4-submissions opened 11 months ago
bytes032 marked the issue as primary issue
141345 marked the issue as sufficient quality report
Working as intended - a malicious paymaster can “take” the fee that user gave it, and fail transaction on purpose.
miladpiri (sponsor) disputed
GalloDaSballo changed the severity to QA (Quality Assurance)
I believe that due to scoping, we cannot determine a Medium Severity
The paymaster is a contract that works in tandem with the AA contract, the Paymaster could cause issues, but it may also not
Lines of code
https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L703
Vulnerability details
Impact
Gas Manipulation Risk in Paymaster-Enabled Transactions can lead to unexpected transaction failures, even when users provide sufficient gas and fees, due to potential gas consumption manipulation by malicious paymasters. This may result in user funds being spent without achieving the desired transaction outcome.
Proof of Concept
Imagine there's an L2 transaction in which the fee is covered by a paymaster. During validation of this transaction, all the remaining gas (referred to as
gasLimitForTx
) is directed to the functionZKSYNC_NEAR_CALL_validateTx
using the "near-call" mechanism. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1204Now, within the
ZKSYNC_NEAR_CALL_validateTx
function, after thevalidateTransaction
is called in theDefaultAccount
contract, the functionensurePayment
is invoked to manage the transaction fee payments. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1302Since the paymaster address is not empty, the function
validateAndPayForPaymasterTransaction
is triggered. This function, in turn, callsvalidateAndPayForPaymasterTransaction
in the paymaster contract. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L703 https://github.com/code-423n4/2023-10-zksync/blob/main/code/contracts/zksync/contracts/TestnetPaymaster.sol#L14Since the paymaster implementation is out of scope, to put it simply, the paymaster transfers tokens from the user to themselves and forwards the equivalent ETH amount to the Bootloader as the transaction fee. https://github.com/code-423n4/2023-10-zksync/blob/main/code/contracts/zksync/contracts/TestnetPaymaster.sol#L50 https://github.com/code-423n4/2023-10-zksync/blob/main/code/contracts/zksync/contracts/TestnetPaymaster.sol#L63
It's worth emphasizing that the gas allocated to the paymaster is determined by the remaining gas from the
gasLimitForTx
parameter, which is initially set using thenear-call
mechanism. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L2034 https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1204The issue arises when the gas consumption within the paymaster exceeds expectations or if the paymaster intentionally consumes a substantial amount of gas. In such cases, there's a risk that the gas allocated for execution might not be sufficient, leading to execution failure.
For example, Alice initiates an L2 transaction with
gasLimit = 10,000,000
and intends to allocate 6,000,000 gas to the destination address. During validation, thenear-call
mechanism forwards10,000,000 - X
gas toZKSYNC_NEAR_CALL_validateTx
, whereX
represents the gas consumed between lines 1292 and 1300. For simplicity, let's assumeX = 1,000,000
. Consequently, the remaining gas available for theensurePayment
function is 9,000,000. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1292-L1300When
ensurePayment
is invoked, it calls thevalidateAndPayForPaymasterTransaction
function within the paymaster, forwarding 9,000,000 gas. If the paymaster, for some reason, intentionally consumes 8,999,000 gas, upon returning to thel2TxValidation
function,gasLeft
is set to just 9,000,000 - 8,999,000 = 1,000. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1216In the subsequent execution, having only 1,000 gas left, it's insufficient to execute the transaction as Alice originally intended (6,000,000 gas to the destination address). Thus, the transaction fails, and the remaining gas is entirely consumed, leading to a "success = false" result. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/bootloader/bootloader.yul#L1271
Despite Alice providing a sufficient
gasLimit
, paying the fee in a specific token to the paymaster, and the paymaster subsequently covering the required fee in ETH, the transaction fails due to gas misuse by the paymaster. This problem arises because the entire remaining gas is sent to the paymaster, allowing the paymaster to potentially influence gas consumption in a way that leaves insufficient gas for the user's transaction execution to succeed.Tools Used
Recommended Mitigation Steps
Consider implementing either of the following solutions:
maxGasForPaymaster
within theTransaction
structure set by the user. https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/TransactionHelper.sol#L25Assessed type
Context