xanoutas/solidity-security-findings huggingface dataset:
Risk of Unintended Fund Loss due to Default Refund Recipient Handling
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L283


# Vulnerability details

## Impact
***In _requestL2Transaction, if address(0) is specified as the refund recipient, msg.sender is used by default. However, the msg.sender address will not be controllable by contracts on L2, so any refund, or the bridged ETH amount in case of a failed transaction, will be lost.***

***Here is a breakdown impact***
`1. Loss of Funds:` ***When a transaction on L2 is initiated by a contract and specifies address(0) as the refund recipient (which triggers the default behavior of using msg.sender), any refunds or bridged ETH amounts that should be returned in case of a failed transaction will be sent to the contract's address. This means that the original sender, which could be a user or another contract, does not receive the refund, resulting in a loss of funds.***
`2. Inability to Control Refunds:` ***Contracts on L2 do not have control over the msg.sender address. Therefore, they cannot direct the refund to the intended recipient. This lack of control can lead to unexpected financial losses for users and contracts.***

## Proof of Concept
[Affected code area](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L310).
***Let's say a contract on L1 initiates a transaction to perform an action on L2. This transaction includes a refund recipient set to address(0) (the default behavior). The transaction on L2 fails, and a refund should be issued. However, because the refund recipient is msg.sender (the L2 contract), the refund goes to the L2 contract's address. The original sender from L1 does not receive the refund, resulting in a loss of funds***

## Tools Used
Manual Review
## Recommended Mitigation Steps
```
function _requestL2Transaction(address _target, uint256 _value, address _refundRecipient) internal {
    require(_refundRecipient != address(0), "Invalid refund recipient"); // Disallow address(0) as refund recipient
    require(!_isContract(_refundRecipient), "Refund recipient must be an EOA");

    // Rest of the transaction execution logic
}
```
***In this code, we first check if the refund recipient is not set to address(0) and then verify that it is not a contract address. If either condition is not met, the transaction is reverted, ensuring that funds are only returned to valid externally owned accounts***


## Assessed type

Token-Transfer
ZkSync Does Not Work With Anonymous Events
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/test/EventWriter.spec.ts#L40


# Vulnerability details

## Impact
[`EventWriter.yul`](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/EventWriter.yul) does not work with anonymous events. This could cause EVM compatibility issues when using innocent Solidity code and leads to unexpected behavior. ["Known differences from Ethereum"](https://era.zksync.io/docs/reference/architecture/differences-with-ethereum.html) does not say anything about not having the capabilities to work with anonymous events. 

This unexpected behavior caused by anonymous events can be seen in the [EventWriter test](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/test/EventWriter.spec.ts#L40). By removing the `.skip` and running the test it will fail.

## Tools Used
Manual

## Recommended Mitigation Steps
Consider adding additional functionality to `EventWriter.yul` to be able to handle anonymous events. In the meantime, add more details to [Known differences from Ethereum](https://era.zksync.io/docs/reference/architecture/differences-with-ethereum.html) around not being 
compatible with anonymous events.


## Assessed type

Other
Elliptic curve addition without complete validation leaves room for an attacker to be possibly using points that lie on a twist of the desired curve or in the wrong subgroup
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/precompiles/EcAdd.yul#L1-L441


# Vulnerability details

## Summary

The `EcAdd` object seems to be handling elliptic curve operations, specifically point addition. While the functions seem to follow the elliptic curve addition rules, there are possible pitfalls regarding certain edge cases.

## Vulnerability Detail

While the code does check for points at infinity and performs the elliptic curve addition, it may not handle all edge cases related to elliptic curve cryptography. For example, there's a check for points lying on the curve, but there isn't a clear check for twist security, ensuring that points lie on the right curve or subgroup.

More specifically, the function `isOnFieldOrder(coordinate)` checks if the coordinate is less than the prime `P()`. While this ensures that the point's coordinates lie within the field, it still doesn't ensure that the point lies on the respective curve.

## Impact

An attacker may be able to exploit the math operations using points that lie on a "twist" of the desired curve or in the wrong subgroup, which can lead to unintended behavior or leakage of private data.

## Proof of Concept

To exploit this, an attacker would basically need to send points that lie on a twist of the curve or in the wrong subgroup and observe the behavior of the CA to eventually glean information or cause unintended results.

## Tools Used

Manual review + GPT for further text enhancements.

## Recommended Mitigation Steps

Introduce further validation checks to ensure that the points used in the `EcAdd` operations lie on the correct curve and subgroup. Also, consider using libraries or external CAs that have been thoroughly reviewed and tested for elliptic curve operations.

```yul
// ... [rest of the functions remain unchanged]

function pointIsOnCurveAndNotTwist(x, y) -> ret {
    // Ensure the point lies on the curve and not on its twist.
    let ySquared := montgomeryMul(y, y)
    let xSquared := montgomeryMul(x, x)
    let xQubed := montgomeryMul(xSquared, x)
    let xQubedPlusThree := montgomeryAdd(xQubed, MONTGOMERY_THREE())

    // This is the curve equation check.
    ret := eq(ySquared, xQubedPlusThree)
}

// ... [rest of the code]

if or(iszero(pointIsOnCurveAndNotTwist(x1, y1)), iszero(pointIsOnCurveAndNotTwist(x2, y2))) {
    revert(0, 0)  // Revert the transaction instead of burning gas.
}
```

This modification ensures that the points are on the curve and not its twist. It also replaces the gas burning with a tx revert, providing a clearer error to the caller.





## Assessed type

Invalid Validation
Arbitrary Code Execution via ZKSync Near Call
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1417-L1477


# Vulnerability details

## Impact
the use of the pop function in this line: https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L1428C23-L1428C57  and the mismanagement of the paymaster variable. the problem is arise  from the improper handling of the paymaster variable and the use of the pop function. The paymaster is not properly validated and can be set to a malicious contract. The pop function, when called with a malicious paymaster, allows an attacker to drain the contract's balance by exploiting the pop function. The attacker can repeatedly call the pop function in a recursive manner, draining the contract's balance.
## Proof of Concept

here is a reproduce of the vulnerability how can be exploited by an attacker:
- to  Deploy the vulnerable contract and the malicious paymaster contract on the blockchain.
- and after the attacker Call the function in the vulnerable contract that he sets the paymaster to the address of the malicious paymaster contract. This can be done by providing the paymaster address as a parameter in a transaction.
- After the paymaster is set, the attacker  call the function in the vulnerable contract that triggers the pop function. This can be done by providing the function name as a parameter in a transaction.
- and The pop function in the malicious paymaster contract will be executed, causing a recursive call and draining the contract's balance.

## Tools Used
manual review
## Recommended Mitigation Steps
 the paymaster variable should be properly validated. If the paymaster is not set, the pop function should not be called, and a check should be added to ensure that the paymaster is not a malicious contract.


## Assessed type

Other
Users can double their deposited funds through the finalizeWithdrawal function of the L1WethBridge contract
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L233-L268
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L192-L216
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L113-L120


# Vulnerability details

## Impact

The [finalizeWithdrawal function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L233) of the L1WethBridge contract allows for a user to finalize the withdrawal process of their funds. This logic works by checking if the L2 withdrawl of the funds has been completed by querying the [`isEthWithdrawalFinalized`](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L245) state variable mapping of the zkSync contract. If the withdrawal has been finalized, the [proveL2MessageInclusion function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L253) is invoked to verify the withdrawl, and, if it has not be finalized, the [finalizeEthWithdrawal function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L257) is invoked to initiate the withdrawl. This function will ultimately invoke the [\_withdrawFunds function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L213) which will [transfer](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L117) the `_amount` value to the `_l1WithdrawReceiver` address. What is problematic is that the L1WethBridge contract will then perform the [same transfer](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L261-L263) allowing the user to double their funds with each withdrawl. A malicious user can then use this vulnerability to drain the protocol of it's ETH directly impacting user funds and the general functionality of the protocol.

## Proof of Concept

The [finalizeWithdrawal function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L233) will invoke the [finalizeEthWithdrawal function](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contract
Insecure Withdrawal Verification in L2 Contract
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L192-L217


# Vulnerability details

## Impact
in the finalizeEthWithdrawal function does not verify that the withdrawal was actually initialized on L2 before transferring the funds. This can lead to a situation where funds are transferred from the contract to the receiver, even though the withdrawal was not actually initialized on L2
here the vulnerable part :
```solidity
 function finalizeEthWithdrawal(
        uint256 _l2BatchNumber,
        uint256 _l2MessageIndex,
        uint16 _l2TxNumberInBatch,
        bytes calldata _message,
        bytes32[] calldata _merkleProof
    ) external override nonReentrant senderCanCallFunction(s.allowList) {
        require(!s.isEthWithdrawalFinalized[_l2BatchNumber][_l2MessageIndex], "jj");

        L2Message memory l2ToL1Message = L2Message({
            txNumberInBatch: _l2TxNumberInBatch,
            sender: L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR,
            data: _message
        });

        (address _l1WithdrawReceiver, uint256 _amount) = _parseL2WithdrawalMessage(_message);

        bool proofValid = proveL2MessageInclusion(_l2BatchNumber, _l2MessageIndex, l2ToL1Message, _merkleProof);
        require(proofValid, "pi"); // Failed to verify that withdrawal was actually initialized on L2

        s.isEthWithdrawalFinalized[_l2BatchNumber][_l2MessageIndex] = true;
        _withdrawFunds(_l1WithdrawReceiver, _amount);

        emit EthWithdrawalFinalized(_l1WithdrawReceiver, _amount);
    }
```

## Proof of Concept
here is a scenario of the attack:
- An attacker calls the finalizeEthWithdrawal function with a _l2BatchNumber and _l2MessageIndex that correspond to a non-existent withdrawal.
- The function does not find a corresponding withdrawal in the L2 logs, so it does not verify that the withdrawal was actually initialized on L2.
- The function proceeds to transfer the funds to the receiver specified in the _message parameter.
- The attacker now has the funds that were intended for the receiver.
here is a poc 
```solidity 
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "./MailboxFacet.sol";

contract Test {
    MailboxFacet public mailboxFacet;
    uint256 public initialBalance;
    uint256 public _amount;

    constructor(address _mailboxFacetAddress) {
        mailboxFacet = MailboxFacet(_mailboxFacetAddress);
        initialBalance = address(this).balance;
    }

    function testFinalizeEthWithdrawal() external payable {
        _amount = msg.value;

        // Call the finalizeEthWithdrawal function with a non-existent withdrawal
        mailboxFacet.finalizeEthWithdrawal(
            1, // _l2BatchNumber
            1, // _l2MessageIndex
            1, // _l2TxNumberInBatch
            "0x1234", // _message
            new bytes32[](1) // _merkleProof
        );

        // Check if the funds were transferred to the
Possibly incorrect fiat shamir transform
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L821


# Vulnerability details

##Impact

The recursive proofs are not used in the Fiat-Shamir transform.
If the recursive proofs are freely chosen variables, this would allow arbitrary manipulation of the final verification.

##Proof of Concept

Consider the transcript initialized here:
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L821

The variables u,z,v are generated pseudo randomly from the calldata. 
However, the recursive proofs, [P1] and [P2] are not used when generating the challenges.

Here the public input is loaded:
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L523

And here the points [P1] and [P2] are assigned:
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L674

But in the transcript only the public input is used to generate challenges.

The documentation here seems to imply this is ok:
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L1626

But, any caller of the contract can generate his own value for P1 and P2, there is no constraint linking it to the public input. Indeed the public input is only 1 element of F_r while the recursive proof is two points on the curve.

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Verifier.sol#L495

##Attack vector

If I wanted to verify an invalid proof, I would first submit invalid proof, follow the verification generating challenges to get u,z,v, then calculate a [P1] and [P2] that will satisfy the final check.

I can do this because u,z,v don't get updated when [P1] and [P2] are changed. See a similar vulnerability here:

https://blog.trailofbits.com/2022/04/18/the-frozen-heart-vulnerability-in-plonk/

##Recommendations
Include the recursive parts in the transcript.





## Assessed type

Math
The operator can still refund the user the full amount of gasLimit which could possibly lead to a damage for zkSync revenue
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L916-L918
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L921-L925


# Vulnerability details

## Impact

In zksync, `requestL2Transaction`  can be used to send a L1->L2 TX. 
```solidity
    function requestL2Transaction(
        address _contractL2,
        uint256 _l2Value,
        bytes calldata _calldata,
        uint256 _l2GasLimit,
        uint256 _l2GasPerPubdataByteLimit,
        bytes[] calldata _factoryDeps,
        address _refundRecipient
    ) external payable nonReentrant senderCanCallFunction(s.allowList) returns (bytes32 canonicalTxHash) {
```
[Mailbox.requestL2Transaction:L236-L245](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L236-L245)


When the bootloader receives the L1->L2 TX, it does the following:
1. Calculates the L2 gas limit for the transaction's body, i.e. without intrinsic costs and overhead.
```solidity
	let gasLimitForTx, reservedGas := getGasLimitForTx(
		innerTxDataOffset, 
		transactionIndex, 
		gasPerPubdata, 
		L1_TX_INTRINSIC_L2_GAS(), 
		L1_TX_INTRINSIC_PUBDATA()
	)
```
[bootloader:L878-L884](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L878-L884)

**gasLimitForTx** => is the gas limit for the transaction's body.
**reservedGas** => is the amount of gas that is beyond the operator's trust limit to refund it back later. 
Note: the operator's trust limit is guaranteed to be at least `MAX_GAS_PER_TRANSACTION` which is at the moment 80000000 (check SystemConfig.json). 

2. Ensure that the deposited eth covers `txInternalCost` and `value`
```solidity
	let gasPrice := getMaxFeePerGas(innerTxDataOffset)
	let txInternalCost := safeMul(gasPrice, gasLimit, "poa")
	let value := getValue(innerTxDataOffset)
	if lt(getReserved0(innerTxDataOffset), safeAdd(value, txInternalCost, "ol")) {
		assertionError("deposited eth too low")
	}
```
[bootloader:L901-L906](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L901-L906)

3. Checks if **gasLimitForTx** is greater than `gasUsedOnPreparation` then calls `getExecuteL1TxAndGetRefund` to attempt to execute the TX. This function returns a potentialRefund if there is any.

```solidity
		if gt(gasLimitForTx, gasUsedOnPreparation) {
			let potentialRefund := 0

			potentialRefund, success := getExecuteL1TxAndGetRefund(txDataOffset, sub(gasLimitForTx, gasUsedOnPreparation))

```
[bootloader:L908-L912](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/bootloader/bootloader.yul#L908-L912)

So, the `po
total zksync feature can be freeze from `MailboxFacet` 
Audit: 2023-10-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L280


# Vulnerability details

## Impact
the main important modules in the project structure is how demand proxy and its facets are used and i was worried about `MailboxFacet` as there is no unit-test exist for it,
**"I found inhomogeneity for what `MailboxFacet` status saved and the actual happen in the full cycle"**
and therefore the amount for `s.totalDepositedAmountPerUser` for ETH token will not have the actual value incase failure or withdraw which will always increased until exceed the `depositCap` so in this case no longer deposit can be made until update `depositCap` , which also will not actually reached as `s.totalDepositedAmountPerUser` is not holding the actual value.

in this issue also **recorded amount larger than the depositor actually did , will give him right to take it, specially if the `L1ERC20Bridge` contract changed relay on `MailboxFacet` status in other deployment, and `withdraw` or unpredictable issue **

## Proof of Concept
### - Incase of failure
as any unexpected revert happens outside `MailboxFacet` may cause the issue, here in the following trace is for test for `ExecutorFacet` transaction failed which its `revert` will not  reset the status variable value `s.totalDepositedAmountPerUser` in `MailboxFacet` contract ,which means `MailboxFacet::requestL2Transaction` can be attacked by increase to `AllowList::depositCap` limit to freeze the service and reject any transaction later to it.

reuse `ExecutingTest::test_RevertWhen_ExecutingWithUnmatchedPriorityOperationHash` for example 
```
Logs:
  mailbox.totalDepositedAmountPerUser
  10000500000000000000

Traces:
  [450685] ExecutingTest::test_mailbox_totalDepositedAmountPerUser_whenRevert_ExecutingWithUnmatchedPriorityOperationHash() 
    ├─ [0] VM::prank(owner: [0x7c8999dC9a822c1f0Df42023113EDB4FDd543266]) 
    │   └─ ← ()
    ├─ [47003] AllowList::setDepositLimit(0x0000000000000000000000000000000000000000, true, 20001000000000000000 [2e19]) 
    │   └─ ← ()
    ├─ [0] VM::prank(validator: [0x9d5c72Ad9b728cae519e74DA03eF7eb140332Ee3]) 
    │   └─ ← ()
    ├─ [24969] DiamondProxy::revertBatches(0) 
    │   ├─ [19147] ExecutorFacet::revertBatches(0) [delegatecall]
    │   │   ├─ emit BlocksRevert(totalBatchesCommitted: 0, totalBatchesVerified: 0, totalBatchesExecuted: 0)
    │   │   └─ ← ()
    │   └─ ← ()
    ├─ [1087] Utils::randomBytes32(0x61726269747261727943616e6f6e6963616c547848617368) [delegatecall]
    │   └─ ← 0x83c9d1cb03606fe79b39ce699104c8d0d7b396145e5eab6eb6984fdb1fb36e0e
    ├─ [6620] Utils::createSystemLogs() [delegatecall]
    │   └─ ← 
    ├─ [294] Utils::packBatchTimestampAndBlockTimestamp(31536001 [3.153e7], 31536001 [3.153e7]) [delegatecall]
    │   └─ ← 
    ├─ [1082] Utils::constructL2Log(true, 0x000000000000000000000000000000000000800B, 3, 0x00000000000000000000000001e133810000000000