code-423n4 / 2023-11-zetachain-findings

0 stars 0 forks source link

Pay Gas in ERC20 functionality will get frontran #577

Closed c4-bot-8 closed 9 months ago

c4-bot-8 commented 9 months ago

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%.
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 tokens and adjust the amount according to the after-swap state in the CCTX.

2. case common.CoinType_ERC20

The other case is slightly different but the flaw is the same. Here we can pay with any ERC20 token.

  1. Quote the ZRC20 / gasZRC20 pair in order to get the amount feeInZRC20 to supply in order to get outTxGasFee of gasZRC20 tokens which is gasPrice * gasLimit + protocolFee . Please note that this time, the protocolFee is applied and that the gasPrice is this time not multiplied by 2. This may lead to some issues because the gas may be too low, but the protocolFee should in theory play a buffer role to avoid these issues.
  2. Mint the feeInZRC20 from the relevant ZRC20 contract
  3. Swap the feeInZRC20 of ZRC20 in order to get at least outTxGasFee as the received amount in gasZRC20
  4. Burn the returned gasZRC20 received from the swapping operation and adjust the CCTX parameters

In both cases, an attacker may frontrun these UniswapV2 quotes by selling the token that the node will swap right after in order to make the price of the asset to swap lower, and sell just after to profit from the price difference paid by the node. The operation will also put the user in a net loss which for instance in the case 2 wanted to pay the withdrawal of their gas tokens in ERC20 which will mint too much gas tokens (denominated as ZRC20) and profit the attacker.

Tools Used

Manual

Recommended Mitigation Steps

Add a deadline and a slippage that is close from the current market price.

Assessed type

MEV

c4-pre-sort commented 9 months ago

DadeKuma marked the issue as duplicate of #507

c4-pre-sort commented 9 months ago

DadeKuma marked the issue as sufficient quality report

c4-judge commented 8 months ago

0xean changed the severity to 2 (Med Risk)

c4-judge commented 8 months ago

0xean marked the issue as satisfactory