Secret Network / Axelar Bridge CW20-ICS20 Source-Channel Validation Omission / Unbacked saToken Mint (2026-06-10)
- Name: Secret Network / Axelar bridge CW20-ICS20 source-channel validation omission / unbacked saToken mint
- Date: 2026-06-10
- Protocol: Secret Network / Axelar
- Chain: Secret Network + Axelar + IBC
- Category: bridge verification failure / deposit-signal-locked-value mismatch / message-source authentication failure / unbacked wrapped-asset mint
- Loss: about
$4.67Macross saUSDT, saUSDC, saDAI, saWETH, saWBTC, saWBNB, and sawstETH - Summary: The Secret↔Axelar bridge did not fail because Axelar’s validator set, IBC light clients, or escrow accounts were directly compromised. It failed because the Secret-side
ics20-for-axelarcontract stopped proving that an inbound IBC packet belonged to Axelar’s canonical channel before minting Secret-wrapped Axelar assets. Two upstream CW20-ICS20 safety checks—parse_voucher_denom(&msg.denom, &packet.src)andreduce_channel_balance(...)—had been commented out in a forked Secret contract and never replaced. That left the bridge asking onlyis this bare denom allow-listed?instead ofdid this packet come from the right channel, and does that channel actually have escrowed backing for this redeemability domain?. The sharper design failure was thatsame bare denom namestarted to outranksame source channel and same escrow bucket, so packets from an attacker-created channel could mint the exact same saTokens that the real Axelar return lane later treated as fully backed inventory. The attacker therefore spun up a fake Cosmos chain, opened a fresh IBC channel to the Secret-side bridge contract, self-relayed forged deposits carrying allow-listed bare denoms, minted unbacked saTokens, and then redeemed those counterfeit balances back over the legitimate Axelar channel to release real escrowed assets. - Trigger / entry point:
- Secret’s bridge-side contract
secret1yxjmepvyl2c25vnt53cr2dpn8amknwausxee83was a modified fork of Secret’s CW20-ICS20 / SNIP-20 bridge logic, tailored for the Axelar connection. - The deployed code minted Secret-wrapped Axelar assets on inbound IBC receive.
- In
do_ibc_packet_receive, the fork no longer enforced the denom-path / source-channel consistency check and no longer reduced channel balance before minting. - The remaining logic allowed minting when the incoming denom was a bare allow-listed name such as
uusdtoruusdc, even if the packet arrived over an attacker-controlled IBC channel rather than Axelar’s canonicalchannel-61/channel-69link.
- Secret’s bridge-side contract
- Root cause:
- The bridge’s authenticity boundary drifted from
packet proven to come from Axelar's escrow-backed channeltopacket carries an allow-listed bare denom string. - The removed
parse_voucher_denom(...)check meant the contract no longer verified that the voucher denom trace matched the packet’s actual source endpoint. - The removed
reduce_channel_balance(...)check meant the contract no longer capped minting to balances genuinely escrowed for that same channel. - Together, those omissions collapsed channel-local accounting into a denom-only identity rule:
uusdt over attacker channel-227anduusdt over Axelar's canonical channel-69/channel-61 routeminted the samesaUSDTeven though only one path had real backing. - Because IBC channel creation is permissionless, the attacker did not need to counterfeit Axelar. They only needed a new chain and a new channel to Secret.
- Once the Secret-side contract minted real-looking saTokens from those forged packets, the legitimate return path over the real Axelar channel faithfully released real assets from Axelar escrow.
- The bridge’s authenticity boundary drifted from
- Exploit path:
- The attacker spent June 10 preparing the route: rehearsing IBC setup, creating test light clients and channels, and probing the exit path.
- At
19:01 UTC, the attacker created live Secret client07-tendermint-244for fake chainibc-dev-allowlist-denom-1. - Between
19:02and19:05 UTC, they openedconnection-241andchannel-227from that fake chain to the Secret-side bridge contractsecret1yxjmepvyl2c25vnt53cr2dpn8amknwausxee83. - Between
19:14and19:20 UTC, the attacker self-relayed seven forged inbound IBC packets overchannel-227, each using a bare allow-listed denom and each sized to the current real backing of one saToken class. - Because the contract only checked
supported_denomand did not bind that denom to Axelar’s canonical source channel or escrow, it minted unbackedsaUSDT,saUSDC,saDAI,saWETH,saWBTC,saWBNB, andsawstETHto attacker addresssecret154pdez8zazqh26wyuyu70nqraal3hkvf2f03vm. - The forged mint transactions included approximately
1,121,270 USDT,638,198 USDC,893 WETH,16 WBTC,174,536 DAI,170 WBNB, and17 wstETHworth of counterfeit saTokens. - Between
19:33and19:36 UTC, the attacker redeemed those freshly minted saTokens back over the legitimate Secret→Axelar channel to Axelar addressaxelar1hzra9z4zn8q0w8f3dj2wnw0xgetu8dfdhl6ad8. - Axelar’s real escrow for channel-69 then unescrowed the matching genuine assets, because from Axelar’s perspective the saTokens arriving over the legitimate return path looked normal.
- From
19:38to19:56 UTC, the attacker moved the haul off Axelar in 18 packet-forwarded IBC transfers via Osmosis, then bridged onward to Ethereum. - On Ethereum, the funds were consolidated in wallet
0x6c2eAB82bA2897A6E99FB6Af018020dA15123976, sold into ETH, split into roughly 30 transfers, and later deposited at exchanges before the bridge shortfall was discovered.
- What the attacker needed to be true:
- The Secret-side contract had to mint on inbound IBC receive without proving the packet came from Axelar’s canonical channel.
- Bare denom allow-listing had to be treated as equivalent to authenticated source provenance.
- Permissionless IBC channel creation had to remain available, so the attacker could open a brand-new channel from a chain they controlled.
- The bridge had to keep one shared saToken identity per denom name rather than segregating
same denom name over different channelsinto distinct redeemability domains. - The real Secret→Axelar redemption path had to accept the counterfeit saTokens as ordinary bridge inventory and release real escrow accordingly.
- Monitoring had to miss the escrow depletion long enough for the attacker to finish cross-chain exits and exchange deposits.
- Technical anatomy worth remembering:
- This is a clean
permissionless IBC is fine until an application-layer bridge forgets channel provenanceincident. IBC itself lets anyone open new channels; the application contract still has to keep each channel’s accounting isolated. - The broken boundary was not
did a packet exist?; it wasdid this packet arrive from the one channel whose escrow is allowed to back this voucher?. - Removing
parse_voucher_denom(...)turned denom text into a weak authenticity signal. Removingreduce_channel_balance(...)removed the quantitative cap that would have limited minting to actually escrowed inventory for that same channel. - The attacker did not need to forge Axelar-origin packets under Axelar’s light client. They used a legitimate new IBC connection plus the bridge contract’s own underconstrained mint semantics.
- Secret’s privacy model compounded detection delay. The bridge shortfall was only noticed on June 17, when a routine transfer tried to unescrow more WBTC than remained in Axelar’s escrow account.
- This is a clean
Three bridge-truth promotions this incident collapsed
- A packet carrying a familiar bare denom existed at all
- The attacker did not need to forge IBC transport or counterfeit Axelar validators. They only needed a real packet on a fresh attacker-controlled channel whose payload used an allow-listed bare denom like
uusdtoruusdc. - At this stage the strongest true fact was only
some packet with familiar denom text arrived, notAxelar's canonical escrow-backed route authored a mintable deposit claim.
- The attacker did not need to forge IBC transport or counterfeit Axelar validators. They only needed a real packet on a fresh attacker-controlled channel whose payload used an allow-listed bare denom like
- That packet was promoted into canonical route-authenticated mint truth
- This was the decisive collapse. Once
parse_voucher_denom(&msg.denom, &packet.src)was gone, the contract stopped binding denom trace to the packet’s actual source channel; oncereduce_channel_balance(...)was gone, it also stopped proving that the same route had escrow headroom to support the mint. - The bridge therefore confused
an allow-listed denom string exists on some IBC routewiththe canonical Axelar channel proved this exact redeemability claim, so attacker channelchannel-227could mint the samesaUSDT,saUSDC,saWETH,saWBTC,saWBNB,saDAI, andsawstETHthat the real Axelar route used.
- This was the decisive collapse. Once
- That accepted mint truth still deserved immediate release authority against real Axelar escrow
- Once the counterfeit saTokens existed, the legitimate Secret→Axelar redemption lane treated them as ordinary bridge inventory and unescrowed real assets from Axelar custody.
- The route preserved too little separation between
a wrong-channel packet already polluted shared saToken supplyandthat shared supply can now burn for real assets on the healthy channel, which is why one source-authentication failure on Secret could empty multiple live escrow buckets on Axelar.
The review habit this incident punishes is simple: do not stop at the packet was valid IBC. First ask whether the packet proved the exact channel-local backing object that was allowed to mint a shared wrapper, then ask whether that wrapper should have inherited immediate release authority against the canonical escrow lane.
- Representative transactions, contracts, and addresses:
- Exploited Secret contract:
secret1yxjmepvyl2c25vnt53cr2dpn8amknwausxee83 - Original deployed code ID:
872 - Migrated code ID at exploit time:
2446 - Migration transaction to code ID 2446:
A049548A7DEE3F9BF38A97F70EF8051E680A95F1088EBFE3D3C30BFEACADB8B5 - Attacker Secret address:
secret154pdez8zazqh26wyuyu70nqraal3hkvf2f03vm - Fake source-chain sender string used in forged packets:
cosmos1lquy33kxj7ywgzwvc662sqv4y0hyns4a8na5x2 - Attacker Axelar address:
axelar1hzra9z4zn8q0w8f3dj2wnw0xgetu8dfdhl6ad8 - Main Ethereum consolidation wallet:
0x6c2eAB82bA2897A6E99FB6Af018020dA15123976 - Live attacker client creation on Secret:
562C87C05E5523A07E5F5221CC996EE5C4FF3DE80E2E7A6CC5D2030C52E0E386 - Live attacker channel open init (
channel-227):D404B1BF6611344D2C1C72444989AFCD7C2C264FA2F83855ED5A0CD514D4970E - Live attacker channel open ack:
C0D8B4B4322C56F895E4C182817755B263337C3FEDC3D50A267E048C944293DD - Representative forged mint transactions on Secret:
- saUSDT:
E17184D49A12B594CEB66696F380598E83C2E716F5F10002291B8DA1C2B1BFA1 - saUSDC:
DC4529F30175A7FAEF332FEEE86A5FE1F4D9DE56B144A549CCE620E64039BB42 - saWETH:
4A0CC8681376AEA9E52C5812E661F880B207061799E6CF84D50B7FDD4C9283A0 - saWBTC:
AE894827FEA3421DCA4F593765D5C7B7602C8CBF5E2994894F031BD13C4BC351
- saUSDT:
- Representative Secret→Axelar redemption transactions:
- USDC redemption:
842CCB7514F4F7527E10C590C32526DDF4CFBC3CCA099B3B5EE14901AB5B3A18 - WETH redemption:
45C2E2853A3DEBC558DFDB5E0316F79B6D3122A2EB0455059FD4744F7E857B64 - WBTC redemption:
D0BF6C27028D574591DFC25E4D68E0889BE41727265489752F35DBBBF702C3C2
- USDC redemption:
- Exploited Secret contract:
- Response / mitigation:
- Axelar disclosed the incident publicly after the June 17 failed transfer exposed the escrow shortfall.
- Axelar’s Emergency Committee disabled the Secret↔Axelar connection.
- Squid removed Secret from its frontend.
- Secret said the impact was confined to the Axelar-linked saToken set on this connection and that other IBC channels, assets, and Axelar core protocol components were not affected.
- Secret and Axelar coordinated incident response, notified law enforcement, and began migration / remediation work on the affected Secret-side contract.
- Comparable exploit pattern:
Pattern tags
- deposit-signal-locked-value-mismatch
- message-source-authentication-failure
- bridge-verification-failure
- Secret’s forked
ics20-for-axelarcontract letsame bare denom nameoutranksame source channel and same escrow bucket, so packets from attacker channelchannel-227minted the same saTokens as the real Axelarchannel-69/channel-61route - Two commented-out guards —
parse_voucher_denom(&msg.denom, &packet.src)andreduce_channel_balance(...)— removed both provenance binding and route-local backing limits, turning a permissionless new IBC channel into mint authority over Axelar-backed inventory - The legitimate Secret→Axelar redemption path did not need its own bug; once wrong-channel packets minted ordinary-looking
saUSDT,saUSDC,saWETH,saWBTC,saWBNB,saDAI, andsawstETH, the real return lane honored them as if they had come from the canonical escrow-backed route
Why the exploit worked
- Common Prefix’s reconstruction makes the decisive break unusually explicit: in
do_ibc_packet_receive, the deployed Secret fork had commented out bothparse_voucher_denom(&msg.denom, &packet.src)andreduce_channel_balance(...), leaving only a bare-denom allow-list check for inbound minting. - That changed the bridge’s source-authentication question from
did this packet come from Axelar's canonical channel with matching denom trace and escrow?to the weakerdoes this packet carry an allow-listed bare denom like uusdt or uusdc?. - Because IBC channel creation is permissionless, the attacker did not need to counterfeit Axelar or subvert IBC light clients. They only needed a new chain, a new Secret-side client, and a fresh channel whose packets the contract was still willing to treat as fungible with the Axelar lane.
- The missing
reduce_channel_balance(...)check mattered as much as the missing source-channel check. Even after wrong-channel packets minted saTokens, a healthy route-local balance cap could have blocked those counterfeit balances from expanding to the full escrowed backing of the real channel. - The exit path then behaved exactly as designed: once the attacker held ordinary-looking saTokens, redeeming them over the legitimate Secret→Axelar channel caused Axelar’s real escrow accounts to unescrow matching genuine assets. The bridge’s lie had already been authored on the Secret-side mint path.
- The incident is therefore best preserved not as
IBC was permissionlessor merelyallow-list bug, but asroute-local redeemability collapsed. The bridge forgot that the same bare denom name over two different channels is not the same liability unless channel identity, denom trace, and escrow bucket are all proven to line up.
Detection and audit pivots
- Audit IBC bridge forks for any path where
parse_voucher_denom, channel-trace validation, or equivalent source-endpoint checks were removed, bypassed, or replaced with denom-only allow-listing. - Treat channel-local escrow accounting as part of authenticity, not just bookkeeping. If
reduce_channel_balance(...)-style caps disappear, one wrong-channel mint can often scale directly to the full backing of the canonical route. - Ask whether the bridge keeps
same asset name over different channelsroute-local all the way through minting, holding, and redemption, or whether wrapper names likesaUSDTsilently collapse multiple channels into one redeemability domain. - Test the bridge the way the attacker did: stand up a disposable chain, open a fresh permissionless IBC channel, and try to send allow-listed bare denoms or path variants that should be non-fungible with the production route. If the resulting wrapped asset can redeem against live escrow elsewhere, the bridge is already broken.
- Reconcile wrapped supply against source-channel backing at route granularity, not only at the aggregate token-symbol level.
Total saUSDT supplycan look reasonable whilechannel-227 minted supplyis entirely unbacked. - Review migrations and long-lived forks as if they were verifier upgrades. Common Prefix’s timeline makes clear that the vulnerable checks had been absent since the repo’s early 2023 history and survived the March 2026 migration to code ID
2446;unchanged for yearsis not evidence that route-local authenticity was safe. - In privacy-preserving wrappers, require explicit reserve and escrow observability outside the hidden-balance layer. The exploit itself took minutes, but the shortfall was only surfaced when later unescrow attempts exceeded real Axelar backing.
Why this incident matters
- It preserves a valuable bridge-security lesson distinct from classic proof forgery: a bridge can fail even when transport-layer proofs are valid if the application contract forgets which channel is allowed to mint which asset.
- It is a strong exemplar for
deposit signal / locked-value mismatchin IBC form. The forged packets were real packets, but they still did not prove the locked value Axelar later released. - It also preserves the sharper deposit-truth lesson that
same denom textmust not collapse different source channels into one redeemability domain unless the bridge has actually proven those channels share the same backing bucket. In healthy IBC-style bridge semantics,same asset nameis never enough on its own. - It also sharpens the separate blast-radius question preserved in bridge bounded release design: once bare denom allow-listing became mint authority, one forged channel still had immediate line-of-sight to the full live escrow buckets behind
saUSDT,saUSDC,saWETH,saWBTC,saWBNB,saDAI, andsawstETHrather than first hitting tighter per-channel or per-asset release ceilings. - It also shows why privacy-chain operations need explicit reserve monitoring. The exploit itself took minutes; the detection lag took a week.
Failure-prevention checklist
- Keep
channel identity + denom trace + escrow bucket + wrapped representationfused as one redeemability object from inbound packet validation through mint, holding, burn, and release.Bare denom allow-listedshould never be enough to mint a shared wrapped asset. - Enforce source-path validation at the application layer even when transport proofs are valid. In this case,
parse_voucher_denom(&msg.denom, &packet.src)or an equivalent invariant needed to remain a hard mint precondition, not an optional convenience check. - Preserve route-local balance accounting like
reduce_channel_balance(...)as part of authenticity, not mere bookkeeping. A healthy bridge should fail closed when the inbound route cannot prove it has the escrow headroom it is trying to mint against. - Default new channels and route additions to isolated liability buckets, fresh wrappers, or low-cap quarantine until operators explicitly prove they are entitled to share an existing saToken redeemability domain.
- Key monitoring, reconciliation, and pause controls by channel / route as well as by token symbol. Operators should be able to answer
which channel authored this saUSDT supply?before any large redemption is honored. - Require route-local or per-asset release caps on the canonical exit path so one wrong-channel mint cannot immediately redeem the full backing of the healthy channel.
- Review long-lived forks and migrations like verifier upgrades. If a fork has removed route-binding checks, carrying that code unchanged into a later migration is still preserving an authenticity break.
- In privacy-preserving wrappers, publish enough reserve-side telemetry or reconciliation proofs that wrong-channel liabilities cannot hide inside opaque token balances until an escrow account runs dry.
Audit prompts
- What exact tuple defines redeemability here: bare denom, full denom trace, source port/channel, escrow bucket, wrapped-token contract, or some stricter canonical object?
- Can a new permissionless IBC channel or peer mint into an existing wrapped representation without first proving it shares the same backing and unwind path as the canonical route?
- Where is
same asset name on a different channel is a different liability unless proven otherwiseenforced, and is that enforcement shared across mint, burn, and release logic? - If
parse_voucher_denom(...)-style checks disappeared tomorrow, what independent control would still prevent wrong-channel packets from minting spendable inventory? - Does route-local accounting survive all code paths, or can a packet mint against aggregate token identity without consuming balance from the specific channel whose escrow is supposed to back it?
- Can operators reconcile wrapped supply against escrow per channel, or do they only see aggregate
saUSDT/saUSDCtotals that hide route contamination until redemption fails? - If one wrong-channel mint lands anyway, what per-route pause, cap, delay, or quarantine keeps the legitimate return lane from immediately honoring the counterfeit inventory?
- During migrations, forks, or wrapper customizations, which tests prove the deployed code still binds voucher denom trace, packet source, channel-local balance, and redeemability domain together?
Lessons that should already be standard
- Permissionless transport is not the bug. The bug is letting a permissionless new route inherit the reserve semantics of an existing canonical route just because metadata looks familiar.
- In channel-based bridges, route identity is asset identity.
uusdt over attacker channel-227anduusdt over Axelar channel-69/channel-61are not the same bridge liability unless the bridge proves they share one backing object. - Route-local accounting is part of authentication. Losing the quantitative cap that ties minting to a specific channel’s escrow can be as fatal as losing the source-channel check itself.
- Shared wrappers are dangerous when they flatten failure domains. One user-facing token like
saUSDTcan quietly become a balance-sheet merger between healthy and malicious routes unless the bridge resists that aliasing explicitly. - Privacy features increase the need for explicit reserve observability. Hidden balances do not excuse weak route-local reconciliation when the wrapped asset can still redeem against visible escrow elsewhere.
- Long-lived forks deserve the same suspicion as fresh changes. A missing invariant that survived from 2023 into a 2026 migration was still a live bridge-authenticity defect, not legacy harmlessness.
- Bounded release is a separate layer. Even after route authenticity fails once, the healthy bridge question is why the legitimate exit lane was still allowed to release nearly the full live backing of seven assets in minutes.
Internal linkages
- More specific recurring pattern: deposit-signal-locked-value-mismatch.
- Adjacent provenance framing: message-source-authentication-failure.
- Parent bridge-wide framing: bridge-verification-failure.
- Positive-design companion: bridge-deposit-truth-binding — the sharper framing for why the bridge needed one canonical
channel + denom trace + escrow bucket + redeemability domainobject before any inbound packet could mint saTokens. - Positive-design provenance companion: bridge-source-provenance-bundle-and-authoring-surface-discipline — useful for the even narrower
who exactly was allowed to speak for the Axelar route?question once the contract started treating bare denom allow-listing as a substitute for channel-local authorship. - Positive-design companion: bridge-route-local-redeemability-domain-discipline — useful for the narrower question of why
same bare denomwas ever allowed to collapse multiple channels into one saToken balance sheet in the first place. - Positive-design blast-radius companion: bridge-bounded-release-design — useful for the separate question of why one wrong-channel mint path was still allowed to redeem so much real Axelar escrow so quickly.
- Useful contrast: qubit-finance-qbridge-zero-value-deposit-event-spoof-2022-01-27 — Qubit emitted a fake deposit event without locking value, while Secret minted against real IBC packets that arrived from the wrong channel and therefore carried no claim on Axelar escrow.
Control surface
-
The decisive leverage sat in Secret’s forked
ics20-for-axelarcontract: inbound packet handling, denom allow-listing, source-channel validation, per-channel escrow accounting, and the mapping fromdenom textto shared saToken redeemability once minted. -
Axelar’s destination-side escrow release logic did not need an additional bug. By the time real assets were unescrowed, the falsehood had already been authored inside the Secret-side bridge mint path.
-
The sharp healthy-side question is worth preserving explicitly: which exact channel, denom trace, and escrow bucket were allowed to author the saToken that the real Axelar return lane would later honor?
-
The adjacent control failure is that channel-local mint truth and balance-sheet reach were too tightly coupled: once the Secret contract accepted
same bare denom, wrong channelas redeemable saToken inventory, the legitimate Axelar return lane still exposed large immediately spendable escrow with too little route-local friction. -
Sources:
- https://forum.scrt.network/t/security-incident-axelar-secret-ibc-bridge-exploit-june-10-2026/7995
- https://www.commonprefix.com/blog/secret-network-exploit
- https://rekt.news/secret-network-rekt
- https://github.com/scrtlabs/ics20-for-axelar/tree/b2061822376a772da0e55bbf9436797b9d0356b7
- https://github.com/scrtlabs/snip20-ics20
- https://x.com/axelar/status/2067965810174214451
-
Last reviewed: 2026-08-02 UTC