Gravity Bridge Denom-Mapping Registry Poisoning Drain (2026-05-29)
- Name: Gravity Bridge denom-mapping registry poisoning drain
- Date: 2026-05-29
- Protocol: Gravity Bridge
- Chain: Gravity Bridge + Osmosis + Ethereum
- Category: bridge verification failure / registry poisoning / token-identity aliasing / metadata-trust failure
- Loss: about $5.4M across USDC, USDT, WETH, and PAXG
- Summary: Gravity Bridge did not lose funds because its Ethereum custody contract signatures were forged. It lost funds because the bridge’s token-identity registry was poisoned before those signatures were ever requested. The attacker minted worthless Osmosis tokenfactory assets, IBC-transferred them onto Gravity chain, then called Ethereum-side
deployERC20()with fabricatedcosmosDenomstrings that embedded the real Ethereum custody-token addresses as path segments. Public analysis indicates the resultingMsgERC20DeployedClaimflow registered those real custody-token addresses as the canonical Ethereum representation of the fake Cosmos-side denoms. OnceDenomToERC20Lookupwas poisoned, legitimate validator-signed outgoing batches paid real USDC, USDT, WETH, and PAXG to the attacker against worthless source-side balances. - Trigger / entry point:
- Gravity’s Ethereum contract
0xa4108aa1ec4967f8b52220a4f7e94a8201f2d906exposed a permissionlessdeployERC20()path for Cosmos-originated assets. - The function accepted
_cosmosDenomas an arbitrary string and emitted it verbatim inERC20DeployedEvent. - Gravity chain’s
handleErc20Deployedpath checked that the claim carried a structurally valid Ethereum address and metadata matching the denom, but did not cross-check the claimed token contract against the existing ETH-originated custody-token registry before writing canonical mappings. - The attacker first registered a minimal Gravity validator presence (
gravityvaloper1rdwckpx2p3mwu4k8xdz8vmd4x3mqx4v9ars8k) with 80 GRAV so its orchestrator could submit claims alongside legitimate validators.
- Gravity’s Ethereum contract
- Root cause:
- Gravity treated denomination identity as trustworthy even though the attacker could shape the string that bridged identity between Cosmos-side denom state and Ethereum-side token registration.
- The bridge’s permissionless token-registration path accepted attacker-controlled denom strings without restricting them to the actual
ibc/HASHidentity the destination chain had assigned. - The Gravity-chain attestation handler then accepted a claim path that public analysis says ended up registering real Ethereum custody-token addresses for those fake denoms, while metadata checks were satisfied because the attacker had created predictable IBC metadata on purpose.
- The most conservative preserved lesson is therefore not
validators signed malicious withdrawals; it isvalidators signed withdrawals against a registry the attacker had already taught to lie about asset identity. - One technical detail remains publicly incomplete: open-source analysis shows the permissive input and the missing registry cross-check clearly, but the exact mechanism by which the fabricated denom path caused
token_contractin accepted claims to resolve to the real custody-token addresses instead of the freshly deployed wrapper contracts still awaits a fuller official postmortem.
- Exploit path:
- The attacker registered Gravity validator
julia666, self-delegating 80 GRAV so its orchestrator could participate in claim submission. - On Osmosis, attacker address
osmo1m9athjzah02f2mnrgtcke7e5ya3zpvw8lccussminted four worthless tokenfactory assets designed to mirror USDC, USDT, WETH, and PAXG. - Those fake tokens were IBC-transferred to Gravity chain over Osmosis
channel-144/ Gravitychannel-10, where each received a normalibc/HASHdenom plus predictable bank metadata. - On Ethereum, attacker address
0x73e95ae5f3b87e02d4547afe86d0d466e9450d6bcalleddeployERC20()four times, passing fabricated_cosmosDenomstrings that embedded the real custody-token contract addresses as path components. - Example: the fake USDC path embedded real USDC contract
0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48inside the denom string instead of using only the plainibc/HASHidentifier. - Validators observed those
ERC20DeployedEventemissions and submittedMsgERC20DeployedClaimattestations on Gravity chain. - Public analysis indicates
handleErc20Deployedthen poisoned the denom-to-ERC20 registry so the fake denoms mapped to the real custody-token addresses rather than to the fresh wrapper contracts. - The attacker next submitted
MsgBatchSendToEthClaimwithdrawals for the fake Cosmos-side assets. DenomToERC20Lookupreturned the poisoned real-token addresses, validators signed outgoing batches41572through41575, and Ethereum relayers executed legitimate batch withdrawals into attacker wallet0x7b582033061b96cc3f9421e73a749ed7c62da1f9.- Real custody was released:
4,349,701 USDC,434,072 USDT,274.34 WETH, and14.16 PAXG, after which part of the haul moved toward laundering address0xc8c71ae4261e55a66d9967f2ac252be4e669f562and secondary EOA0x4d3ca32e687e871a58b78acac73be59ac37c7a47.
- The attacker registered Gravity validator
- What the attacker needed to be true:
- A minimal validator registration had to be enough to satisfy the claim-submission gate even without meaningful voting weight in Ethereum valset signatures.
- The bridge had to accept permissionless token registration on Ethereum for Cosmos-originated assets.
_cosmosDenomhad to remain an attacker-controlled free-form string rather than a validated canonical denom identity.- Gravity-chain attestation processing had to trust metadata compatibility and address format more than canonical asset-origin cross-checks.
- Once the registry was poisoned, validator-signed outgoing batches had to treat the false mapping as normal truth and release real custody without a second balance-sheet sanity check.
- Technical anatomy worth remembering:
- This is a strong bridge-verification-failure case where the attacker did not forge signatures, bypass the batch verifier, or steal validator keys. The attacker changed what the bridge believed those honest signatures referred to.
- The exploit path is a reminder that asset registries are part of bridge authenticity. If token identity can be aliased, honest validator signatures can still authorize fake withdrawals.
- The metadata checks were not a defense because the attacker controlled the source-side token creation and therefore the predictable IBC metadata that the bridge later treated as confirmation.
- Gravity had the shape of a cleaner trust model than many multisig bridges, but this incident shows that bridge integrity can still fail in the translation layer between source-chain denoms, token metadata, and destination-side custody asset identity.
- The missing official postmortem matters. Public evidence is already enough to preserve the registry-poisoning lesson, but the remaining uncertainty about the exact claim-construction path is part of the historical record rather than something to smooth away.
- Representative transactions, claims, and contracts:
- Gravity Ethereum contract:
0xa4108aa1ec4967f8b52220a4f7e94a8201f2d906 - Attacker Ethereum deployer address:
0x73e95ae5f3b87e02d4547afe86d0d466e9450d6b - Attacker validator:
gravityvaloper1rdwckpx2p3mwu4k8xdz8vmd4x3mqx4v9ars8k - Attacker orchestrator Ethereum key:
0x91b52e07132a49dad1b7a3939a51547f79468ec7 - Validator-creation transaction:
F6AA34E8D01C55A5F1E38310E816DBB4BEE25B2FDB29ACD7ED1E25352BA62009 - Fake USDC deploy transaction:
0x92b2a61d88b3f68d7d3b5cba4a12f53e0c226287702d72de80a60515987e3532 - Fake USDT deploy transaction:
0x364ebefab8e7c8ddd425447349cc99dcd75ab1751612f7ef9c60c20fdb9a2bb6 - Fake WETH deploy transaction:
0x871433af303743d61b3f2a38174e9e6655fdc8937c0689122685509dcabbf066 - Fake PAXG deploy transaction:
0x67a181c333cb75231613c125215168a1eacbb4021d07cf0d91ca8bbaa65f8245 - Accepted Gravity-chain
MsgERC20DeployedClaimtransactions:- USDC:
786C41F0B9DA6EB8F59A1BC3299504AE47EFC4BBAB13673E391E307DB1BCEE49 - USDT:
9DF358F96E2BDE91FA96D1CC1CBEBE42E5CA2DD2F5745CD2ADA2591751101A9D - WETH:
388112D6C4BEDD74D34157AEC689195B942A8EDC4F476C4230C638CDF399E499 - PAXG:
14BC7A377F959973613473A5EFB623D4011AED86BE00D68B782DB6D6720F9A37
- USDC:
- Primary theft wallet:
0x7b582033061b96cc3f9421e73a749ed7c62da1f9 - Secondary holding wallet:
0x4d3ca32e687e871a58b78acac73be59ac37c7a47 - Laundering / Tornado-funded address highlighted in public tracing:
0xc8c71ae4261e55a66d9967f2ac252be4e669f562
- Gravity Ethereum contract:
- Response / mitigation:
- Gravity Bridge told validators to halt validators and orchestrators while the incident was investigated.
- The bridge was paused, stopping further Ethereum↔Cosmos transfers.
- Public discussion after the halt remained thin; outside researchers reconstructed the registry-poisoning path faster than the team published a formal root-cause writeup.
- At review time, the open record still lacked a full official postmortem explaining the claim-construction step that turned the attacker-controlled denom path into accepted real-token contract addresses.
- Current upstream Gravity source does, however, make the missing boundary much more explicit than the incident-era public explanations did:
handleErc20Deployed()now callsvalidateCosmosOriginatedMapping()before metadata checks and then rejects pre-existing mappings in both directions throughgetCosmosOriginatedERC20ForDenom()andgetCosmosOriginatedDenomForERC20(). Even without a polished postmortem, that later hardening preserves the core lesson: origin admission and destination-reserve reservation must fail closed before metadata compatibility or governance-whitelisting logic gets a vote.
Three asset-identity promotions this incident collapsed
-
A Cosmos-side asset-looking object existed at all
- The attacker minted worthless Osmosis tokenfactory assets, pushed them over IBC, and gave Gravity a superficially normal
ibc/HASHdenom plus metadata trail to look at. - That should only have meant
a candidate foreign asset has appeared, notthis thing is already meaningful bridge identity.
- The attacker minted worthless Osmosis tokenfactory assets, pushed them over IBC, and gave Gravity a superficially normal
-
That candidate was promoted into canonical Cosmos-originated identity for this exact bridge mapping
- Gravity’s Ethereum-side
deployERC20()accepted attacker-authored_cosmosDenomstrings, andhandleErc20Deployedaccepted a claim flow whose shallow checks focused on address shape and metadata compatibility. - In practice, the bridge let a user-shaped denomination path answer the much stronger question
what exact source-side asset identity is this?.
- Gravity’s Ethereum-side
-
That admitted identity inherited live redeemability over an already-funded Ethereum reserve path
- Once
DenomToERC20Lookupwas poisoned, later validator-signedMsgBatchSendToEthClaimwithdrawals no longer needed to do anything exotic; they just spent against the bridge’s new canonical answer. - That is the step that turned a bad registration event into real USDC, USDT, WETH, and PAXG release. Gravity collapsed
candidate asset discovered,canonical identity admitted, andexisting reserve now redeemable by that identityinto one operational path.
- Once
Pattern tags
- asset-identity-registry-poisoning
- bridge-verification-failure
- The decisive break was not forged validator signatures or a fake withdrawal proof; it was the earlier moment when
deployERC20()plushandleErc20Deployedlet attacker-authored denom identity become canonical bridge asset identity. - Attacker-controlled IBC metadata satisfied the bridge’s compatibility checks, so
looks like the right token metadatawas allowed to stand in for the much stronger claimthis denom is actually entitled to this destination-side reserve path. - Once
DenomToERC20Lookupwas poisoned, every later batch withdrawal became structurally ordinary. Honest validators and relayers kept authorizing the bridge’s own corrupted answer. - The preserved invariant is therefore
new source-side asset discoveredmust stay separate fromexisting destination reserve may now be redeemed by this asset; Gravity collapsed those two states into one registry write.
Why the exploit worked
- The attacker never had to defeat Gravity’s Ethereum batch-signature scheme. They only had to corrupt the lookup table that told those honest signatures which Ethereum asset a Cosmos-side denom redeemed.
- Gravity’s permissionless
deployERC20()path accepted_cosmosDenomas an unconstrained string and emitted it verbatim into the event stream. That gave the attacker a place to smuggle real custody-token addresses into what should have been only a canonical denom identifier. - On the Gravity-chain side, the acceptance logic prioritized
structurally valid Ethereum addressplus metadata compatibility over the stronger questionis this denom actually allowed to point at an already-live ETH-originated reserve?Because the attacker had created the Osmosis tokenfactory assets and their IBC transfer path, the downstream bank metadata was predictable enough to satisfy those checks by construction. - The exploit therefore lived in the bridge’s long-lived asset-identity state, not in one later withdrawal packet. After the registry write, the bridge had already learned the wrong economic meaning: fake Cosmos-side balances now appeared to redeem real USDC, USDT, WETH, and PAXG.
- That is why the downstream batches looked so normal.
MsgBatchSendToEthClaimand Ethereum-side execution were not individually malformed; they consumed a registry the attacker had already taught to lie. - The still-missing official postmortem matters because one claim-construction detail remains open, but it does not change the core preserved lesson: registry truth and reserve entitlement were insufficiently fused, so semantic corruption upstream became ordinary custody release downstream.
Detection and audit pivots
-
Review every
asset registrationortoken deploymentpath as if it were already a custody path. If a registry write can later decide which live reserve a withdrawal touches, then that write deserves the same adversarial review as a direct unlock function. -
Check guard ordering, not just guard existence. Gravity is a good reminder that the strongest questions should run first: does this candidate denom even qualify as a cosmos-originated identity, and is the destination token already reserved in either direction, before any metadata or governance-acceptance path can make the asset look legitimate.
-
Treat metadata matching as weak evidence only. Name/symbol/decimals compatibility, especially when source-side metadata is attacker-created or IBC-derived, should never by itself authorize a mapping into an already-live custody token.
-
Diff both directions of asset identity before writing canonical mappings:
denom -> token,token -> denom, andis this token already an ETH-originated reserve object?Gravity’s public reconstructions are useful precisely because the missing cross-check appears to have lived right next to the vulnerable path. -
Ask whether downstream withdrawals consume a canonicalized asset-identity object or just the current registry answer. If later batch logic trusts
DenomToERC20Lookupwithout re-proving reserve entitlement, registry poisoning already has direct line-of-sight to custody. -
Keep semantic-state failures separate from signer-compromise analysis. A bridge can have honest validators, valid signatures, and correct relayer execution while still being broken if the shared asset registry no longer preserves one truthful meaning for reserve redemption.
-
Comparable exploit pattern:
Why this incident matters
- It broadens the bridge corpus beyond signer compromise, proof-parser bugs, and event-spoofed deposits into the nastier
registry tells honest components to liebranch. - It shows that bridge asset identity is itself a security boundary. If a fake source-side denomination can become synonymous with a real custody token, downstream validator signatures and batch execution will look perfectly normal while the bridge is already broken.
- It is also a good example of why
permissionless asset registrationis not a harmless UX feature when registry entries later decide what custody asset outgoing batches release. - The preserved healthy-side boundary is sharper than
validate metadata better: discovery of a new Cosmos-side asset should have stayed distinct from the much stronger act of granting that asset rights over Gravity’s already-live USDC, USDT, WETH, and PAXG reserve paths. Bridge asset-identity admission and destination reservation discipline is the positive-design companion for that missing separation. - It also sharpens the separate blast-radius question preserved in bridge bounded release design: once the registry was poisoned, one false identity mapping still had direct line-of-sight to large live USDC, USDT, WETH, and PAXG custody instead of first hitting tighter per-route release limits or slower high-value controls.
Failure-prevention checklist
- Treat asset-registration handlers as custody-critical code. A permissionless
deployERC20()or denom-registration path should only be able to create an isolated wrapper candidate, not silently grant rights over an already-live Ethereum custody token. - Derive canonical asset identity from provenance-first facts — source chain, canonical denom or source contract, route, custody mode, and explicit destination reservation — rather than from free-form strings plus metadata compatibility.
- Reserve destination-native and already-canonical ERC-20 custody contracts up front. New Cosmos-originated asset admissions should fail closed or mint fresh wrappers whenever the destination token address is already tied to an ETH-originated reserve object.
- Split
asset discovered,metadata accepted,wrapper deployed, andasset may redeem existing reserveinto separate lifecycle states with stronger evidence and tighter authority at each step. - Make identity-table writes monotonic and collision-resistant: no aliasing through path formatting, embedded addresses, wrapper/native ambiguity, or later governance/tooling rewrites that can shadow an existing reserve mapping.
- Bind outgoing batch construction and Ethereum release logic to the same canonical identity object used at admission time, and add route-local reconciliation so a fake Cosmos-side denom cannot quietly drain the reserve bucket of a real custody asset.
- Put low initial caps, rate limits, and easy pause authority under newly admitted or newly remapped asset identities so one poisoned registration cannot immediately command full USDC/USDT/WETH/PAXG release authority.
- Log and alert on every denom-to-token mapping mutation, especially any event where a previously unseen Cosmos-side asset would newly touch an established Ethereum custody contract.
Audit prompts
- What exact tuple defines canonical asset identity on this bridge route, and which parts of that tuple can a user influence through denom strings, IBC paths, metadata, or token-deployment helpers?
- Can any permissionless registration path bind a previously unseen Cosmos-side asset to an already-existing Ethereum custody token instead of forcing creation of a fresh isolated wrapper?
- Are
name,symbol,decimals, or IBC metadata ever allowed to do security work that should have required stronger origin-authenticity proof? - Which code path proves that a proposed
denom -> ERC20mapping is not colliding with an ETH-originated reserve object already backing live withdrawals? - If an attacker creates a fake source-side asset that looks operationally identical to USDC or WETH, where is the transition from
looks similartomay redeem this reserveexplicitly denied? - Do later
MsgBatchSendToEthClaimwithdrawals consume one immutable canonical identity object, or do they trust whatever answer the mutable registry currently returns? - What governance, upgrade, or operator action can rewrite asset mappings today, and how quickly could that widen rights over an existing reserve before operators notice?
- If one bad registry entry lands, what per-route cap, pause, withdrawal delay, or downstream reconciliation job stops the next thousand withdrawals from looking ordinary?
Lessons that should already be standard
- Asset registries are bridge verifiers with friendlier names. If they answer the wrong
what does this asset redeem?question, honest validator signatures only automate the theft. Permissionless listingstops being a product feature the moment it can bind new source-side assets to established destination reserves.- Metadata similarity is presentation, not provenance. A bridge that lets
name/symbol/decimals look rightstand in for canonical origin identity is asking users’ UX fields to carry custody authority. - The dangerous state transition is
new asset observedtoexisting premium reserve now redeemable. Safe bridges keep those two states far apart and make the upgrade path explicit. - Monitoring by symbol or aggregate token bucket is too coarse. Operators need per-origin, per-route backing views or poisoned mappings will hide inside apparently normal USDC, USDT, WETH, or PAXG totals.
- Bounded release matters for identity failures too. Even after a registry goes bad, the bridge should not let one false mapping immediately spend deep live custody as if nothing unusual happened.
Internal linkages
- Core recurring pattern: asset-identity-registry-poisoning.
- Sharper subpattern: asset-identity-registry-poisoning — Gravity is the clean
registry already lied about what asset honest withdrawals redeemexemplar. - Positive-design admission companion: bridge-asset-identity-admission-and-destination-reservation-discipline — useful for the healthier
new asset discovered is not yet entitled to an existing destination reserveboundary Gravity failed to preserve. - Positive-design blast-radius companion: bridge-bounded-release-design — useful for the separate question of why one poisoned asset-identity mapping was still allowed to steer so much immediately spendable Ethereum custody.
- Boundary contrast: cross-layer-interpretation-mismatch — useful contrast because Syscoin broke on one payload having two meanings across layers, while Gravity broke because a standing denom-to-token registry had already been poisoned.
- Closest companion incidents: syscoin-bridge-duplicate-asset-commitment-cross-layer-interpretation-mismatch-native-sys-release-2026-06-07, bnb-chain-bsc-token-hub-iavl-proof-forgery-2022-10-06, qubit-finance-qbridge-zero-value-deposit-event-spoof-2022-01-27, and meter-passport-wrapped-native-deposit-path-unbacked-bnb-weth-mint-2022-02-05.
Control surface
-
The decisive leverage sat in Gravity’s denomination-to-token identity layer: permissionless ERC-20 deployment, attestation handling for
MsgERC20DeployedClaim, denom metadata trust, and the registry lookup that later told outgoing batches which Ethereum asset to release. -
More specifically, Gravity collapsed
asset discovered,asset metadata looks compatible, andasset may now point at an already-live Ethereum reserveinto one operational surface. That is the exact admission/reservation seam preserved in bridge asset-identity admission and destination reservation discipline. -
Gravity’s validators did not need to become malicious for funds to leave. They only needed to keep doing their jobs after the registry had already been taught the wrong answer.
-
The adjacent control failure is that the poisoned lookup still had direct authority over large live Ethereum custody. Once the registry answered
this fake denom redeems real USDC/USDT/WETH/PAXG, the bridge had too little route-local friction between semantic corruption and cash-out. -
Sources:
- https://rekt.news/gravity-bridge-rekt
- https://www.quillaudits.com/blog/hack-analysis/gravity-bridge-demon-mapping-poisoning
- https://x.com/gravity_bridge/highlights
- https://raw.githubusercontent.com/Gravity-Bridge/Gravity-Bridge/main/solidity/contracts/Gravity.sol
- https://raw.githubusercontent.com/Gravity-Bridge/Gravity-Bridge/main/module/x/gravity/keeper/attestation_handler.go
-
Last reviewed: 2026-08-02 UTC