Assessment reports>Osmosis Authentication Abstraction>Critical findings>Bypass fee payer authentication
Category: Coding Mistakes

Bypass fee payer authentication

Critical Severity
Critical Impact
High Likelihood

Description

The authenticator's job is to validate the signature of a message and ensure that the required accounts have signed it, including the fee payer if one is specified. When custom CosmWasm authenticators are added (or if an empty AllOfAuthenticator is used) then it is possible for an authenticator to be added that will return iface.Authenticated() regardless of whether the fee payer has signed the message or not:

// Consume the authenticator's static gas
cacheCtx.GasMeter().ConsumeGas(authenticator.StaticGas(), "authenticator static gas")

// Get the authentication data for the transaction
neverWriteCacheCtx, _ := cacheCtx.CacheContext() // GetAuthenticationData is not allowed to modify the state
authData, err := authenticator.GetAuthenticationData(neverWriteCacheCtx, tx, msgIndex, simulate)
if err != nil {
    return ctx, err
}

authentication := authenticator.Authenticate(cacheCtx, account, msg, authData)
if authentication.IsRejected() {
    return ctx, authentication.Error()
}

if authentication.IsAuthenticated() {
    msgAuthenticated = true
    // Once the fee payer is authenticated, we can set the gas limit to its original value
    if !feePayerAuthenticated && account.Equals(feePayer) {
        originalGasMeter.ConsumeGas(payerGasMeter.GasConsumed(), "fee payer gas")
        // Reset this for both contexts
        cacheCtx = ad.authenticatorKeeper.TransientStore.
            GetTransientContextWithGasMeter(originalGasMeter)
        ctx = ctx.WithGasMeter(originalGasMeter)
        feePayerAuthenticated = true
    }
    break
}

This will cause the entire fee to be deducted from the fee payer in the DeductFeeDecorator ante handler, but since the feePayerAuthenticated will not be set to true (account is based off the message's GetSigner, which will not match if a separate fee payer is specified), the amount of gas will be limited to 20,000.

Impact

A malicious user can set up an authenticator to always verify any message, then send messages with high fees and a separate fee payer to drain any account of its funds.

An example POC, which is located in the appendix ref, was provided to Osmosis Labs that demonstrates forcing someone to pay 100,0000 in fees without signing the message:

The POC will output the following:

Balances before:
hacker (osmo1m6a73d0qhl9kphwx84syysnrr3t3myxvhw3f5d): amount: "103875"
victum (osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj): amount: "99362536125"

# transfer log
{
  "type": "transfer",
  "attributes": [
    {
      "key": "recipient",
      "value": "osmo17xpfvakm2amg962yls6f84z3kell8c5lczssa0",
      "index": false
    },
    {
      "key": "sender",
      "value": "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj",
      "index": false
    },
    {
      "key": "amount",
      "value": "1000000uosmo",
      "index": false
    }
  ]
}

Balances after:
hacker: amount: "103875"
victim: amount: "99361536125"

Recommendations

The fee payer should always be authenticated regardless of the authenticator used.

Remediation

This issue has been acknowledged by Osmosis Labs, and a fix was implemented in commit 651eccd9. The feePayerAuthenticated is always authenticated now.

Zellic © 2025Back to top ↑