🛡️ GHSA-6447-269v-g68m — mezod

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-662 OSV
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Mezo: ERC-20 bridgeOut burn can be erased by a stale StateDB overwrite leading to full L1 bridge drain

Note: the fixed version of the validator client has been deployed for some time.

Impact

Potential full drain of L1 bridge without changing bridged balance on Mezo.

Brief/Intro

A malicious user can steal all ERC-20 tokens locked in the L1 bridge by repeatedly calling the bridgeOut precompile from a crafted contract. The precompile's ERC-20 burn executes in an inner StateDB that commits to a cache context, but the outer StateDB still holds stale pre-burn storage slots in its dirtyStorage. When the outer transaction commits, it overwrites the inner burn with stale values -- restoring the attacker's balance and allowance while the AssetsUnlocked event has already been persisted to the bridge store. The Ethereum sidecar observes this event and attests the unlock on L1, releasing real tokens to the attacker. The attacker keeps their Mezo balance intact and can repeat the drain every block.

Vulnerability Details

Mezo's EVM uses a dual-context state architecture for precompile calls. When a precompile is invoked, Contract.Run() calls stateDB.CacheContext() which creates a cachedCtx branched from the base ctx. Cosmos-side state changes (like ERC-20 burns) happen on cachedCtx, while standard EVM storage operations (SLOAD/SSTORE) operate on the outer StateDB's dirtyStorage backed by baseCtx.

The bridgeOut precompile handles two token types with fundamentally different mechanisms:

  • BTC (burnBitcoin): Burns via x/bank (Cosmos native), then explicitly records a journal.SubBalance entry. syncJournalEntries() propagates this to the outer StateDB, keeping both contexts in sync.
  • ERC-20 (burnERC20): Burns via ExecuteContractCall, which creates an entirely new inner StateDB operating on cachedCtx. This inner StateDB executes burnFrom (decreasing balance, supply, and allowance slots), commits its changes to cachedCtx, and is discarded. No journal entry is created. The outer StateDB is never informed of these storage changes.

This creates a critical asymmetry. After burnERC20 returns, the outer StateDB's dirtyStorage still contains the pre-burn allowance (written by approve earlier in the same transaction), and has never loaded the balance or supply slots. When the attacker triggers a subsequent transfer(sink, 1) in the same transaction, the outer StateDB performs a GetCommittedState on the balance slot. This reads from baseCtx -- which holds the stale pre-burn value because cachedCtx changes haven't been flushed to baseCtx yet. The stale balance minus 1 is written to dirtyStorage.

At end-of-transaction, StateDB.Commit() executes in this order:

```go

func (s *StateDB) Commit() error {

if s.flushCache != nil {

s.flushCache() // Step 1: flush cachedCtx → baseCtx (inner burn lands)

}

return s.commit(s.ctx) // Step 2: write dirtyStorage → baseCtx (stale values overwrite)

}

```

Step 1 flushes the inner burn into baseCtx. Step 2 then iterates every slot in dirtyStorage and calls keeper.SetState on baseCtx -- unconditionally, without comparing against current values:

```go

func (s *StateDB) commit(ctx sdk.Context) error {

for _, addr := range s.journal.sortedDirties() {

obj := s.stateObjects[addr]

// ...

for _, key := range obj.dirtyStorage.SortedKeys() {

s.keeper.SetState(ctx, obj.Address(), key, obj.dirtyStorage[key].Bytes())

}

}

return nil

}

```

The stale allowance slot in dirtyStorage (from the approve before bridgeOut) overwrites the zeroed-out allowance that the inner burn wrote. The stale balance slot (from the transfer after bridgeOut) overwrites the zeroed-out balance. The burn is erased.

Meanwhile, SaveAssetsUnlocked was called on cachedCtx during the precompile execution, persisting the AssetsUnlockedEvent to the bridge module's KV store. This event is flushed to baseCtx at Step 1 and survives Step 2 (different KV store prefix, not touched by dirtyStorage). The Ethereum sidecar observes this event and calls AttestBridgeOut on the L1 MezoBridge contract, releasing real tokens to the attacker's L1 address.

The attack is repeatable per-block. Each execution costs only 1 wei (the transfer trigger) while draining the full bridgeOut amount from L1. There is no supply assertion for ERC-20 tokens (only abtc is checked in verifyBTCSupply), so no end-of-block invariant catches the mismatch.

Impact Details

CRITICAL - Using a fixed balance of e.g., 10,000 USDC on mezo an attacker can completly drain the USDC balance on ETH mainnet bridge via repeated exploit transactions on mezo.

The Mezo bridge on L1 (0xF6680EA3b480cA2b72D96ea13cCAF2cFd8e6908c) holds approximatively ~1,753,958.4 USD worth of assets that can be stolen, taking into account offramp limit safeguard. Below a

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. Rated impact: confidentiality none, integrity high, availability high.

Weakness class

GHSA-6447-269v-g68m is classified as CWE-662: Improper Synchronization. The product utilizes multiple threads, processes, components, or systems to allow temporary access to a shared resource that can only be exclusive to one process at a time, but it does not properly synchronize these actions, which might cause…

Affected software

GHSA-6447-269v-g68m is recorded against 1 package.

  • github.com/mezo-org/mezod

Timeline and source

Published on 6 May 2026 and last revised on 25 June 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Package)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N
CWE CWE-662
Public Exploit ✅ No
Source OSV
Published 2026-05-06
Updated 2026-08-20
Modified 2026-06-25
Fix URL N/A

Affected Packages

Software From version Fixed in
github.com/mezo-org/mezod

Similar Threats

Site Security Check

Is mezod part of your stack?

GHSA-6447-269v-g68m is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesGitHub AdvisoryGitHub Advisory Undated