Astroport / Terra IBC Hooks Timeout-Callback Reentrancy Counterfeit-Voucher Drain (2024-07-31)
- Name: Astroport / Terra IBC hooks timeout-callback reentrancy counterfeit-voucher drain
- Date: 2024-07-31
- Protocol: Terra Phoenix chain IBC hooks / Astroport migration liquidity
- Chain: Terra + Axelar + Neutron + Ethereum
- Category: reentrancy / timeout-callback replay / ICS-20 refund duplication / counterfeit IBC voucher mint / cross-chain drain
- Loss: roughly
$6.4Mnominal at incident-time pricing across about58M-60M ASTRO,USDC,USDT, andBTC; public chain analysis traced at least about2.23M USDC,350K USDT,~2 BTC, and tens of millions of counterfeitASTROthrough the exploit path before freezes and force-transfers clawed back part of the inflated ASTRO supply - Summary: Terra Phoenix had reintroduced a critical April 2024
ibc-gobug in a June upgrade. The vulnerable path lived inibc-hooks, the middleware that lets an ICS-20 token transfer memo trigger a CosmWasm callback. The bug let a malicious contract re-enterMsgTimeoutfrom theOnTimeouthook before the packet commitment for that same transfer was deleted. In practice, one timed-out outbound transfer could be refunded repeatedly, so Terra kept minting or releasing the same IBC asset again and again even though only one packet had ever existed. The attacker first proved the path with tiny USDC refunds, then scaled it into large counterfeitUSDC,USDT, andaxlWBTCbalances, and finally used the same primitive against Astroport’s migration-eraASTROinventory on Terra. Those counterfeit assets were then bridged or sold out through real liquidity, turning a chain-level timeout-callback reentrancy bug into a drain of Astroport-linked migration liquidity and other bridged assets. - Trigger / entry point:
- On 2024-04-05, the
ibc-goteam published advisoryASA-2024-007/GHSA-j496-crgh-34mx, warning thatibc-hookscould recursively execute the sameMsgTimeoutbefore packet-commitment deletion and thereby cause unexpected escrow loss or voucher minting. - The issue affected chains that simultaneously used vulnerable
ibc-go, enabled CosmWasm contracts, and wrapped the ICS-20 transfer application withibc-hooks. - Terra had participated in the earlier patch coordination, but public reporting after the exploit said the April fix was accidentally reverted during a June chain upgrade.
- That left Terra’s
ibc-hookstimeout path live again by 2024-07-31, even though the bug class was already publicly documented.
- On 2024-04-05, the
- Root cause:
- Terra’s
ibc-hooksmiddleware let a malicious CosmWasm contract receive control during the ICS-20 timeout path before the packet commitment for that timed-out transfer was deleted. - Because the commitment still existed, the attacker could cause the same
MsgTimeouthandling to execute recursively. - ICS-20 timeout logic is supposed to be single-use: one failed packet should cause one refund, escrow release, or voucher re-mint. Terra’s vulnerable path let that one refund right be consumed multiple times in one transaction flow.
- Once the duplicate timeout executions minted or released counterfeit bridged assets on Terra, the rest of the ecosystem treated them as ordinary balances. They could be swapped locally, bridged out over IBC, or redeemed against migration and pool liquidity as if they were legitimate.
- Terra’s
- Exploit path:
- The attacker funded an Ethereum address
0xD5120B0857CB01CB897B57AEf4a4C4d22dD09A42, used Squid / Axelar routing to move assets toward Cosmos, and prepared Terra-side capital plus Axelar-side gas for later outbound bridging. - On Terra, they instantiated a malicious CosmWasm contract designed to exploit the
ibc-hookstimeout path. - They first ran a small proof transaction using a
7 USDCIBC transfer. According to Rarma’s on-chain reconstruction, the transfer timed out, the hook re-enteredMsgTimeout, and the attacker turned that7 USDCinto28 USDCof refunded balance. - After confirming the path, they increased the fake refund amounts stepwise from
7,000 USDCto28,000 USDC,112,000 USDC, and eventually into much larger values, using the same timeout-callback recursion to mint or release counterfeit Terra-side bridged assets. - They repeated the same pattern for
USDTandaxlWBTC, producing large fake balances from single timeout rights. - With the stablecoin / BTC leg proven, they bought roughly
124,000 ASTROwith6,000 USDC, giving themselves a real starter balance in the migration-eraASTROasset that had been IBC-transferred from Neutron onto Terra. - They then used the same timeout-callback recursion against
ASTRO, ultimately manufacturing about58M-60M ASTROon Terra. - Roughly
33M+ ASTROwas bridged back to Neutron, where about13M ASTROwas swapped intoaxlUSDCand routed onward to Ethereum; a large residualASTRObalance remaining on Terra was later frozen. - The exploit path therefore cashed out in two ways at once: direct counterfeit
USDC/USDT/BTCextraction and secondary monetization of counterfeit migrationASTROthrough Astroport / Neutron liquidity and cross-chain routes.
- The attacker funded an Ethereum address
- What the attacker needed to be true:
- Terra had to be running a vulnerable
ibc-hooks/ibc-gocombination despite the April 2024 advisory. - CosmWasm contract upload and execution had to remain available so the attacker could deploy the malicious callback contract.
- The vulnerable timeout path had to keep packet commitments live long enough for the same
MsgTimeouthandling to be re-entered before refund state was consumed. - The counterfeit Terra-side bridged assets had to remain transferable, swappable, or bridgeable before operators halted the chain.
- Migration or pool liquidity, especially for
ASTRO, had to exist on Terra / Neutron / Axelar routes so the fake balances could be converted into stronger assets.
- Terra had to be running a vulnerable
- Technical anatomy worth remembering:
- This was not a generic
IBC bridge got hackedstory. The decisive failure was a chain-local reentrancy bug inside the timeout callback path ofibc-hookswrapping ICS-20. - The bug let the attacker spend the same refund right more than once before the protocol deleted the state that was supposed to make timeout handling single-use.
- That makes Astroport / Terra a useful cousin to classic EVM reentrancy cases even though the mechanics look different. The callback came from CosmWasm + IBC middleware instead of ERC-777 or ERC-721 hooks, but the invariant break was the same: external control returned before accounting truth was finalized.
- It is also a clean seam case between reentrancy and idempotency failure. The exploit only worked because the timeout path was re-enterable, but what got monetized was a replay of one logically one-shot refund.
- Astroport matters here because Terra still held a large migration-era inventory of Neutron
ASTROon the old chain, so a chain-level counterfeit-voucher primitive could turn directly into protocol-specific token overissuance and market impact.
- This was not a generic
- Representative transactions and contracts:
ibc-goadvisory for the underlying bug:GHSA-j496-crgh-34mx/ASA-2024-007- Attacker Terra wallet:
terra1wrve5z5vsmrgy6ldcveq93aldr6wk3qmxavs4j - Secondary Terra wallet used in exploit execution:
terra1ra4hejz65ss8y52umg7pmerueedtfn9evnln0r - Attacker Neutron wallet:
neutron16wynag7xgfy35sp8c5ls25c0je7dydmvq5pnd8 - Attacker Ethereum cashout wallet:
0xBDe173c4C2249d3a98cD6ed844a4421728114F5A - Representative Terra exploit transaction cited by Rarma:
758F1280F4E8DB47C8C716F29EC418CCEFF52A782377F68AAC33FD92DA36169E - Representative Neutron swap / cashout transaction cited by Rarma:
188D612A00DA692A68F8A52C2EA533CEC3CFC5DA5F73E20D343727F568355C68 - Representative Axelar bridge-out transaction cited by Rekt:
7E28A2BDD3A6DBED27269C23D0BDA2FBE4B2BE7F613E87CC23A237DA473F14E2
- Response / mitigation:
- Terra validators halted the chain for an emergency upgrade once the exploit path was recognized.
- Public updates said validators with more than
67%of voting power upgraded and restarted the chain roughly3.5hours later with the vulnerable path patched again. - Astroport later reported that around
20M ASTROstill sitting on Terra in the attacker’s address was frozen, whileASTRObridged to Neutron was removed from the exploiter wallet through TokenFactory force-transfer powers. - The incident became a pointed ecosystem lesson in regression management: the underlying issue had already been found, coordinated privately, and published publicly months earlier.
Pattern tags
one timed-out IBC refund right stayed live across a user-controlled callback boundarypacket-commitment deletion landed after CosmWasm hook executionknown critical ibc-go fix was reverted in a later chain upgradecounterfeit Terra-side vouchers were accepted downstream as ordinary bridgeable and swappable balancesmigration inventory on a legacy chain became live cash-out capacity for a chain-level counterfeit-mint bug
Why the exploit worked
- Terra’s vulnerable
ibc-hookswrapper authenticated timeout execution with a packet commitment that still existed while theOnTimeoutcallback was running. That meant the chain had not yet consumed the one-shot refund right at the moment it handed control to attacker code. - The attacker did not need to fake IBC verification or forge a source-chain deposit. They only needed one real timed-out packet plus a malicious CosmWasm callback that could recurse into
MsgTimeoutbefore packet-commitment deletion landed. - Once Terra re-minted or released the same voucher repeatedly, downstream systems treated those balances as money-good because voucher authenticity was inferred from local bank / IBC balances, not from immediate escrow reconciliation or route-specific bounded-release checks.
- Astroport’s migration-era ASTRO inventory turned a chain-level middleware bug into protocol-specific loss. Terra still held a large amount of Neutron-origin ASTRO precisely so users could migrate; that stock became the bridgeable, swappable inventory against which counterfeit balances could monetize.
- The incident was also a governance / operations failure, not only a code failure: the bug class was already disclosed and patched upstream, so the decisive mistake was allowing a June upgrade to silently reopen a critical timeout-callback path on a chain that still permitted the callback prerequisites.
Detection and audit pivots
- Trace timeout and acknowledgement flows exactly the way EVM auditors trace withdraw callbacks: where does packet-commitment deletion, refund-right consumption, or nonce invalidation actually occur relative to
ibc-hooks, memo dispatch, or CosmWasm execution? - Reproduce
MsgTimeout -> OnTimeout -> recursive MsgTimeoutin tests with a malicious contract. For this bug family,one timed-out packet cannot create more than one refund or voucher remintis the invariant that matters. - Review every chain upgrade for security-regression risk, not just feature correctness. If a chain previously coordinated around a critical upstream patch, regression tests for that advisory belong in the permanent release gate.
- Inventory the exact preconditions that make the bug live: vulnerable
ibc-go,ibc-hookswrapping ICS-20, and contract-upload / contract-execution rights that let an attacker supply the re-entrant callback target. - Reconcile outstanding voucher supply against escrowed or source-backed value during incident response. A local counterfeit-voucher bug stops being local the moment those balances can still bridge out or hit deep onchain liquidity.
- Treat migration stockpiles and legacy-chain token inventories as blast-radius multipliers. If a deprecated chain still warehouses large balances for user convenience, those balances are part of the live security budget.
Failure-prevention checklist
- Consume timeout rights before callback execution. Packet commitments, refund markers, or equivalent one-shot state should be deleted or irreversibly marked spent before
ibc-hooks, memos, or CosmWasm callbacks receive control. - Make timeout handling prove
second execution == zero additional valueby construction. A recursive or repeatedMsgTimeoutfor the same packet should revert or observe already-consumed state. - Keep one explicit timeout-refund security object per packet: channel, port, sequence, denom, sender, receiver, and refund entitlement should travel together instead of being reconstructed loosely across middleware layers.
- If callback middleware cannot preserve one-shot semantics for a route, fail closed: disable
ibc-hookson ICS-20 timeout paths, narrow supported callback surfaces, or restrict CosmWasm uploads until the route is patched. - Add permanent regression tests for previously disclosed critical advisories.
We patched this onceis not enough if a later upgrade can silently roll the bug back in. - Continuously reconcile voucher liabilities against escrow balances or source-side backing and attach fast circuit breakers to deficits, sudden supply spikes, or impossible timeout-refund patterns.
- Put bounded-release controls under bridged or migration-critical assets: per-denom caps, per-channel throttles, delayed large exits, and operator-run route pauses should assume a counterfeit local balance bug can eventually happen.
- Treat migration inventories on legacy chains as custodial risk, not just UX convenience. Large stranded token stocks should be segregated, time-bounded, or rate-limited if they can be re-bridged or dumped through active liquidity.
- Drill emergency response for chain-local counterfeit-asset events: who halts the chain, who freezes tokenfactory balances, which bridges or DEX routes pause first, and how quickly escrow deficits are measured.
Audit prompts
- In the exact timeout path, where is the packet commitment deleted relative to
OnTimeouthook execution, memo dispatch, and CosmWasm callbacks? - Can a malicious contract cause the same
(channel, port, sequence)timeout to be processed more than once before the commitment is consumed? - Which chain configuration choices are part of the exploit surface: vulnerable
ibc-goversion,ibc-hookswrapping ICS-20, contract-upload permissions, relayer behavior, or channel-specific middleware composition? - If counterfeit local vouchers appear, what concrete controls stop them from being swapped, migrated, or bridged into stronger external assets before operators react?
- Are migration-era token inventories, tokenfactory balances, or legacy-chain reserves ring-fenced, rate-limited, or otherwise segregated from general market liquidity?
- What invariant proves that one timed-out packet can never increase aggregate local voucher supply or escrow release by more than one legitimate refund amount?
- Which release or upgrade tests explicitly replay prior critical advisories, and what would have caught Terra’s regression before mainnet restart?
- During incident response, how quickly can operators measure
voucher supply - escrow backingper denom and attach pauses to the routes that still permit cash-out?
Lessons that should already be standard
- Timeout and refund paths are payout paths. If they move balances back to users, they deserve the same one-shot consumed-state discipline as withdrawals, claims, and bridge releases.
- Cross-domain middleware callbacks are still callbacks. CosmWasm hooks, memos, and IBC timeout handlers should be threat-modeled like ERC-777 hooks or
safeTransferreceivers, not like harmless bookkeeping. - Patch regression is a first-class security failure. Reopening a disclosed critical bug in a later upgrade is not bad luck or ops trivia; it is production reintroduction of a known exploit primitive.
- Local voucher balances are not automatically money-good. If downstream bridges and pools accept them immediately, then counterfeit local minting is already an ecosystem-wide bounded-release problem, not merely a local accounting problem.
- Legacy migration inventories are live treasury. Tokens parked on an old chain for convenience can become the exact liquidity surface that monetizes a chain-level bug.
- Public contract-upload or callback-enablement policy is part of a chain’s effective security boundary. A vulnerability that requires malicious contract code is still catastrophic if the chain intentionally leaves that code path permissionless.
- The clean invariant is brutally simple: one timed-out packet should authorize one refund and then become permanently dead state before any externalized logic can try to spend it again.
Why this incident matters
- It preserves a non-EVM but mechanically clean reentrancy case where bridge middleware, not token standards, supplied the callback window.
- It shows how a chain-level refund bug can become a protocol-specific drain when real liquidity or migration inventory sits downstream of the counterfeit asset.
- It is also a strong reminder that patch regressions are not housekeeping trivia. Reintroducing a known critical callback bug can be just as costly as missing the bug the first time.
Internal linkages
- Core recurring pattern: reentrancy.
- Adjacent pattern: duplicate-claim-idempotency-failure — the same timeout refund right became consumable more than once because the one-shot state delete landed too late.
- Positive-design companion: Exit-right consumed-state binding — one timed-out ICS-20 refund should have been consumed as a single-use exit right before the callback branch could do anything else.
- Positive-design companion: Callback frame truth as a single security object — the timeout callback should have resumed one exact packet / refund frame once, not any recursively reachable
MsgTimeoutexecution that still inherited live commitment state. - Adjacent bridge context: deposit-signal-locked-value-mismatch — once counterfeit vouchers existed, downstream bridge and liquidity paths treated them as if real source-side value had backed them.
- Operational blast-radius companion: Bridge bounded-release design — even a local voucher-counterfeiting bug should have less room to escape if routes, assets, and migration inventories are throttled below ecosystem-drain scale.
Control surface
-
The decisive leverage sat in Terra’s
ibc-hookstimeout callback path, the packet-commitment deletion order inMsgTimeout, CosmWasm contract execution rights, and the downstream Astroport / Neutron / Axelar liquidity routes that accepted counterfeit balances as spendable assets. -
Terra and Astroport did not mainly fail because the attacker found a clever trade. They failed because one timeout refund path was still callable while its single-use accounting truth remained unset.
-
Sources:
- https://github.com/cosmos/ibc-go/security/advisories/GHSA-j496-crgh-34mx
- https://medium.com/@Rarma_/terra-ibc-hooks-exploit-21cca87d70a4
- https://rekt.news/astroport-rekt/
- https://axelarscan.io/transfer/7E28A2BDD3A6DBED27269C23D0BDA2FBE4B2BE7F613E87CC23A237DA473F14E2
- https://www.mintscan.io/terra/tx/758F1280F4E8DB47C8C716F29EC418CCEFF52A782377F68AAC33FD92DA36169E?height=11426425
- https://www.mintscan.io/neutron/tx/188D612A00DA692A68F8A52C2EA533CEC3CFC5DA5F73E20D343727F568355C68?height=12962124
-
Last reviewed: 2026-07-20 UTC