Open c4-bot-7 opened 9 months ago
DadeKuma marked the issue as duplicate of #410
DadeKuma marked the issue as sufficient quality report
0xean marked the issue as satisfactory
Both this submission and #410 allow an observer to maliciously fill the block space by exploiting MsgGasPriceVoter
messages and thus griefing blocks.
However, the issue outlined here demonstrates that resetting the gas meter in the MsgGasPriceVoter
message handler (in keeper_gas_price.go#L184 completely resets the gas meter which may have accumulated gas that has been consumed by previous messages in the same transaction.
Even if the root cause in #410 is fixed, an observer is still able to grief blocks (as demonstrated in this submission).
According to Verdict: Similar exploits under a single issue from the C4 Supreme Court Session, as the root cause in this submission is different from #410, this would make it eligible for being a separate issue.
For the above-mentioned reasons, I would like to request a second look at this submission. Thanks a lot!
Similiar to the last one, I disagree to a certain extent. The issue isn't a single line of code, the issue is that broadly the code doesn't anticipate multiple messages being handle together correctly. That being said, the issue are in different sections of the code and not the same function like the last one, so i will agree to seperate these out.
0xean marked the issue as not a duplicate
0xean marked the issue as primary issue
0xean marked the issue as selected for report
lumtis (sponsor) confirmed
Lines of code
https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/keeper_gas_price.go#L184
Vulnerability details
Impact
ZetaChain blocks can be griefed by a single observer, resulting in the block space being filled with
MsgGasPriceVoter
messages, preventing other transactions from being processed.Proof of Concept
Observers submit the gas prices of the external (connected) chains by sending the
MsgGasPriceVoter
message to ZetaChain. This message is handled in theGasPriceVoter
function.The median gas price is then set in the zEVM's system contract by calling the
SetGasPrice
function in line174
.As this is a call to the zEVM (Evmos) and gas for this EVM call is accounted differently than gas in Cosmos SDK, the gas meter is reset in line
184
by calling theResetGasMeterAndConsumeGas
function to match the EVM's gas consumption, i.e., consume the amount of gas the EVM has consumed instead of the Cosmos SDK gas.This is typically done by Evmos for
MsgEthereumTx
messages, as explained in the Evmos documentation - "Matching EVM Gas consumption".In this instance, the
MsgGasPriceVoter
message (sent by observers), specifically, the consumed gas, does not necessarily have to match the EVM gas model.However, contrary to Evmos, the transaction's gas meter is reset on every
MsgGasPriceVoter
message, ignoring the accumulated gas consumption for multiple messages within a Cosmos SDK transaction (Cosmos allows multiple messages per transaction).Concretely, Evmos uses the accumulated gas used by multiple messages to ensure that the gas meter accounts for the gas consumed by all messages.
As a result, only the gas for the last
MsgGasPriceVoter
message is accounted for, and the gas for all previous messages is ignored, allowing the sender (observer) to send a high number ofMsgGasPriceVoter
messages and only paying the gas for a single message execution.Consequently, a malicious observer can exploit this to grief ZetaChain blocks so that blocks are mostly filled with
MsgGasPriceVoter
messages (or any other messages that consume a lot of gas), preventing other transactions from being processed.This represents a privilege escalation as single observes are not supposed to have such a significant impact on the ZetaChain's block space, and thus, medium severity was chosen.
PoC
This quick and dirty proof of concept should demonstrate that the transaction gas meter only consumes a single
MsgGasPriceVoter
message.Start a local ZetaChain environment with
make start-smoke-test
and wait until everything is startedAccess the
zetacore0
docker container withdocker exec -it zetacore0 sh
Determine the observer address with
zetacored keys show operator
. Pick theaddress
value and replace the subsequentOBSERVER_ADDRESS
placeholder with it.Copy and paste the following bash script into a file called
gas.sh
:chmod +x gas.sh
and run with./gas.sh 100 OBSERVER_ADDRESS
. This will generate a transaction with 100MsgGasPriceVoter
messages and stores it ingas.json
.Sign the transaction
Broadcast the transaction with
zetacored tx broadcast tx.signed.json
Query the transaction hash to determine the used gas. Replace
HASH
with the transaction hash from the previous step.Step 8 should print something like
gas_used: "28704"
(the actual value may vary). This is the total gas used by the transaction. However, this value should be much higher as the transaction contains 100MsgGasPriceVoter
messages. This indicates that the gas meter is reset on everyMsgGasPriceVoter
message (you can verify it by determining the gas usage from a singleMsgGasPriceVoter
)Tools Used
Manual review
Recommended mitigation steps
Consider not resetting the gas meter for
MsgGasPriceVoter
messages.Assessed type
DoS