DEV Community

PandaSecurity
PandaSecurity

Posted on Originally published at panda-security.xyz

Initia Exploit: A MoveVM Cache Bug Emptied Its Bridges

Originally published at panda-security.xyz

On 23 October 2026 at 14:59 UTC, an attacker used a flaw in the script cache of Initia's MoveVM to credit itself with unbacked WETH, iUSD and USDC on the Initia L1. Within about seven minutes, real assets started leaving Ethereum-side bridge reserves. By the time validators halted the L1 and every rollup at 15:41:30 UTC, roughly $2.7M had been bridged out and swapped into 1,135.52 ETH.

No token contract on Ethereum was hacked, and no bridge signature was forged. The bridges did exactly what they were designed to do: they burned a token on Initia and released the matching asset on Ethereum. The problem is that the token on Initia was never backed. This post walks through what is confirmed, what is still unknown, and what bridge and L1 teams should take from it.

TL;DR

  • Victim: Initia, a Cosmos SDK L1 that runs MoveVM and hosts a network of rollups.
  • Root cause (per Initia): a bug in MoveVM's script caching let malicious code "bypass security checks" and spoof VM state, crediting balances without changing the recorded total supply. Initia has not yet published the technical post-mortem.
  • Cash-out: about 105 WETH and ~1.4M AUSD0 over LayerZero, plus ~1.08M USDC through Noble and Circle's CCTP.
  • Proceeds: swapped to ETH and parked in 0x358ef013...41cac8, which still held 1,135.52 ETH and had never sent a transaction when we checked it on 27 October.
  • Who pays: the reserves behind bridged assets. Bitquery measured Noble's USDC reserve for Initia falling from $1.09M to $63, the ETH adapter losing about 96% and the AUSD adapter about 71%.
  • Native INIT: not minted, and Initia says its supply is unaffected.

What Initia Is and Why Bridged Tokens Matter

Initia is a layer 1 built on the Cosmos SDK, with MoveVM as its smart contract engine and a family of rollups that settle to it. Most of the dollar and ether liquidity on the chain is not native. It arrives through bridges:

  • ETH and AUSD come from Ethereum through LayerZero OFT adapters. An adapter locks the real asset on Ethereum, and an omnichain token is credited on Initia. AUSD's Initia-side copy is called AUSD0.
  • USDC comes from Noble, the Cosmos chain that issues Circle's USDC, and leaves for Ethereum through CCTP.
  • iUSD is Initia's own dollar token, issued one-for-one against AUSD0.

The security of every one of these copies reduces to a single invariant: the balances on Initia must never exceed what is locked behind them. If the destination chain can be convinced that someone owns tokens they never deposited, the reserve on the home chain will pay the difference to whoever burns them first.

What Initia Has Confirmed

Initia's official statement on 24 October says an attacker "exploited a bug in Initia MoveVM's script caching that allowed malicious code to bypass security checks." It adds that the attacker "artificially spoofed the MoveVM state," receiving unbacked WETH, iUSD and USDC "without changing the corresponding token's total supply." Co-founder Zon described it as a day-zero bug in Initia's MoveVM on the Cosmos SDK.

That last detail matters for monitoring. The forged balances did not appear in each token's recorded supply. A dashboard that watched only total_supply on Initia would have shown nothing unusual while millions of dollars in balances appeared.

Initia has not said which cache, which check, or which code path was involved. A fix and audit were promised "in a few days," and the L1 and rollups were still halted at the time of writing.

What the Chain Shows

Two independent on-chain write-ups, from D2 Finance (relayed by 0xCERT) and from Bitquery, line up on the shape of the attack.

Preparation

Bitquery reports that the attacker's Ethereum wallet, 0xdd922c84...f6edfa, received 0.098 ETH from Tornado Cash the same morning, sent small test transfers toward Osmosis, and bridged a little ETH to Initia. It then received 141 INIT for gas from an Osmosis wallet and uploaded its own program. The first attempt failed. The second worked.

The forged balances

D2 Finance pointed to a single mint transaction at 14:59:26 UTC that called one Move function to credit 2,000,000 AUSD0, with the same pattern for USDC and ETH. Bitquery counted four runs of the attacker's program within 13 seconds, producing about $7.48M of iUSD, USDC, AUSD0 and WETH at face value. Both agree that nothing was locked anywhere to back it.

Face value overstates the damage. About 3M of that was iUSD, which exists only on Initia and had no bridge to drain. Only the copies of real assets could be redeemed.

The cash-out

We checked the Ethereum side ourselves against a public RPC node:

  • In block 26,256,091, the AUSD OFT adapter 0x38b8b7ca...f200c844 transferred exactly 1,400,000 AUSD to the attacker.
  • In block 26,256,115, the ETH OFT adapter 0x66a503a1...0babbc1bde released exactly 105 ETH.
  • About 1.08M USDC was minted to Ethereum via CCTP after leaving Noble's reserve.

Each batch was swapped to ETH within minutes, mostly through 1inch according to Bitquery. Then, at 15:41:35 UTC, five seconds after the halt, the wallet sent 1,135.52 ETH to a fresh address, 0x358ef013...41cac8. That address had a transaction count of zero and the same balance when we read it on 27 October.

What did not get out

Bitquery found that three USDC withdrawals bounced because Noble's reserve no longer held enough. A final 200,000 AUSD0 message stalled because LayerZero Labs' own verifier did not sign it, and the AUSD adapter blocked it later that day. Roughly $1.55M of copies of real assets, plus the unsellable iUSD, were still on Initia when the chain stopped.

Ordinary users paid as well. After the attacker's withdrawals, eleven normal USDC redemptions drained most of what remained in Noble's reserve, and Bitquery counted 55 later withdrawals that failed before the halt.

Why a Cache Bug Becomes a Mint

Initia has not published the mechanism, so we will not guess at its code. The general failure class is well known to anyone who audits VMs, and it is worth stating plainly.

A Move VM does expensive work before it runs a script: it deserializes the bytecode, runs the bytecode verifier, and checks type and resource-safety rules. Those rules are what stop a script from creating a Coin or FungibleAsset value out of thin air or writing to storage it does not own. Caching the result is a reasonable optimisation. It becomes dangerous when the cache key does not fully bind what was verified. If the key misses the exact bytes, the dependency set, or the context the check depended on, a later execution can reuse a "verified" result for code that was never verified.

In a resource-oriented VM, skipping verification is close to the worst case. Resource safety is the mint authority. Bypass it, and a balance can exist without the module that controls supply ever being asked. That is consistent with Initia's description of balances that appeared while recorded supply did not move.

The lesson for auditors: in VM and runtime reviews, every cache in front of a security check is itself a security boundary. Review what the key contains, when entries are invalidated, and whether a cache hit can ever skip a check that a miss would run.

The Bridge Side: Trust Without Limits

The VM bug created the forged balances. The bridges turned them into $2.7M. A bridge cannot tell a forged balance from a real one if the source chain's state says it is real, so the last line of defense is how much it will release, and how fast.

Here is a simplified, illustrative Solidity sketch of an outflow limit on a lockbox adapter. It is not LayerZero's or Initia's code:

// Simplified illustration only. Not production code.
contract RateLimitedLockbox {
    uint256 public constant WINDOW = 1 hours;
    uint256 public maxOutflowPerWindow;   // e.g. 10% of locked reserves
    uint256 public windowStart;
    uint256 public outflowInWindow;

    function _release(address to, uint256 amount) internal {
        if (block.timestamp >= windowStart + WINDOW) {
            windowStart = block.timestamp;
            outflowInWindow = 0;
        }
        outflowInWindow += amount;
        require(outflowInWindow <= maxOutflowPerWindow, "outflow limit");
        token.transfer(to, amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

With a cap of, say, 10% of reserves per hour, a 1.4M AUSD release out of a larger pool, plus a 105 ETH release out of a reserve that ended 96% drained, would have hit the limit and paused. That would have bought the 42 minutes Initia needed to halt the chain.

It is worth noting what did work. LayerZero's verifier set includes a LayerZero Labs verifier, and the final AUSD message stalled when it did not sign. Noble's reserve could only pay what it held, which capped the USDC loss. Earlier we covered a negative fee that drained the WAX bridge and a Sui bridge claim bot that paid the Cetus attacker. The bugs differ, but each raises the same audit question about a bridge: how much can leave before anyone is able to react?

How to Prevent This

Treat VM caches as part of the trusted computing base

Any cache that stores a verification, type-check or permission result must be keyed by everything the result depended on: the full code hash, dependency versions, and the execution context. Add tests that try to reach execution through a cache hit with code the verifier would reject.

Monitor balances against supply, not just supply

Initia's forged balances left recorded supply untouched. An invariant check that sums balances for bridged assets, or reconciles each bridged token against its remote lockbox, would have flagged the attack in its first block.

Rate-limit bridge releases

Per-asset, per-window outflow caps on lockboxes and mint limits on the destination side turn an unlimited drain into a bounded one. Pair them with an automated pause when a cap is hit.

Require independent verifiers

The one AUSD message that stalled was the one that lacked a signature from an independent verifier. Bridges should require more than one independent party to approve a release, and those parties should be allowed to refuse when the amount looks wrong.

Rehearse the halt

Initia stopped the chain 42 minutes after the first forged balance appeared. That is fast for a full L1 halt, and still long enough to lose $2.7M. Chains with bridge exposure should know exactly who can pause which bridge, and practice doing it.

For a broader pre-launch list, see our smart contract security checklist.

Key Addresses and Transactions

Sources

Closing Thoughts

The Initia exploit is a reminder that a bridge is only as sound as the state of the chain it trusts. A single cache bug in the VM was enough to fake balances, and bridges with no outflow limits converted them into real ETH within minutes. When the full post-mortem lands, we will be watching for exactly what the cache key missed.

Building on Move, running a bridge, or launching an appchain? Contact Panda Security for a review of your VM integration and bridge controls, or see our audit pricing.

Top comments (0)