P.K. SHARMA

Cyber security intelligence, AI governance, practitioner analysis

Breaches and Incidents

Cosmos Labs published the security fix 28 hours before it warned the chains. Six networks were drained of about $5.7 million

An unchecked uint256 underflow in Cosmos EVM sat fixed on a public branch for three months, then shipped in a release whose notes flagged urgency and whose changelog omitted the security backport. The first chain was drained 20 hours and 5 minutes later. The first private notification arrived after that. How the attacker learned of the flaw is still unproven, and a downstream developer had published an exploit path in between.

By Parminder Kumar Sharma · · 9 min read

A brass mechanical counter drum caught mid-rotation in deep shadow, captioned: the fix went public, the chains heard last. 20 hours 5 minutes from that release to the first drain.

The bug is eleven lines of missing arithmetic

Cosmos EVM is the module that gives Cosmos SDK chains an Ethereum-compatible execution layer. Its StateDB models an account as one balance. The Cosmos SDK bank module models a vesting account as two: a spendable portion and a locked one. The EVM is only ever handed the spendable figure, and both x/staking and the staking precompile will let a vesting account delegate its locked portion anyway.

How a locked balance becomes an arbitrary one

HOW A LOCKED BALANCE BECOMES AN ARBITRARY ONETwo modules disagree about what an account holds, and one of them does the arithmetic without checking.STEP 1 · ONE ACCOUNT, TWO VIEWS OF ITCosmos SDK x/bank sees a vesting accountspendable100locked, but still delegatable900total 1,000The EVM StateDB sees one numberbalance100it is handed the spendablefigure, and never sees the restSTEP 2 · DELEGATE MORE THAN THE SPENDABLE, AND THE SUBTRACTION WRAPSdelegate 1,000 via the staking precompileSubBalance(balance: 100, amount: 1,000)no check that balance is greater than amountthe balance wraps to near 2^256uint256 has no negatives, so 100 minus 1,000becomes an almost unbounded numberSTEP 3 · MAKE THE VICTIM WRAP TOO, AND RECONCILIATION DOES THE RESTsend the victim exactly 2^256 minustheir balancetargets were inert, high-balance addressesthe commit burns what they really heldStateDB reconciles to x/bank, minting on apositive delta and burning on a negative oneNet supply change across the transaction: zero.Which is why no supply invariant fires, and why the chain does not notice. Figures above are illustrative.
Mechanism from the GHSA advisory, the Cosmos Labs post-mortem and the fix commit, which adds an underflow guard to SubBalance in x/vm/statedb/state_object.go. The precondition is a chain that permits permissionless creation of vesting accounts; genesis-defined vesting accounts are not a route in. The amounts shown are illustrative, chosen to make the arithmetic legible.
The precondition is a chain that permits permissionless creation of vesting accounts. Genesis-defined vesting accounts are not a route in.

The fix is a guard that should have been there from the start:

// x/vm/statedb/state_object.go
	balance := s.Balance()
	if balance.Lt(amount) {
		panic(fmt.Sprintf(
			"state balance underflow for %s: have=%s sub=%s",
			s.address.Hex(), balance.String(), amount.String(),
		))
	}
	return s.SetBalance(new(uint256.Int).Sub(balance, amount))

Eleven lines added, one removed, in a single function. Note the panic string, because it matters later: it names the bug in plain English to anyone reading the diff.

Behaviour differs by release line, which is why the outcomes differed by chain. On 0.6.x the mint or burn lands on the backing ledger, and a large mint triggers a supply overflow that halts the chain. On 0.7.x balances are set directly in x/bank, so nothing halts.

The timeline is the story

Three months on a public branch, then twenty hours

  1. 25 Apr 2026

    Reported through the bug bounty

    The proof of concept targets a 6-decimal chain. Cosmos Labs cannot reproduce it on 18-decimal networks, and every known production chain is 18-decimal, so it is assessed as no fund-loss risk on live networks.

  2. 15 May 2026

    Fixed on the public main branch

    Handled under the silent patch process for issues that do not threaten funds. Deliberately not backported, because the change is state-breaking and would require a coordinated upgrade. It sits publicly readable for three months.

  3. early Aug 2026

    The assessment is overturned

    Further independent reports let the team reproduce it broadly. Cosmos Labs confirms every Cosmos EVM chain is affected regardless of decimals. The exact date is not stated in the post-mortem.

  4. 19 Aug, 23:01

    Backport released publicly

    v0.6.2 and v0.7.2 ship. The release notes say the release contains important security fixes to be applied as soon as possible. The changelog lists two unrelated pull requests and omits the security backport.

  5. 20 Aug, 07:16

    A downstream developer publishes the exploit path

    A Push Chain pull request describes the vulnerability and how to exploit it, crediting an independent audit finding. It does not mention the eight-hour-old releases.

  6. 20 Aug, 19:06

    First chain drained

    MANTRA loses 720.9 million OM from the burn address and a genesis-era multisig. Twenty hours and five minutes after the public release.

  7. 21 Aug, 03:36

    First private notification

    A secure email reaches the security contact list, about two hours after MANTRA reported being exploited and roughly 28 and a half hours after the fix was published publicly.

  8. 22 to 25 Aug

    Five more chains

    TAC and KiiChain are drained on the 22nd. The halt-immediately recommendation goes out at 23:45 that night. Exploitation ends on 25 August.

Dates and times from the Cosmos Labs post-mortem, the GitHub releases API, and the commit history of the backport pull requests. Times are UTC.

Two corrections to the circulating account, both checkable against the repository.

The backport is dated 19 August, not 13 August. The commits landing pull requests 1253 and 1254 onto the release branches are timestamped 19 August at 16:49 and 17:53 UTC. The 13 August date appears to come from the boundary of a date range in the post-mortem's own timeline table, and it has been repeated as though it were the backport date.

The two supporting fixes were not omitted from the advisory. Reporting has told operators to apply two further changes the advisory leaves out. Both shipped in v0.6.1 and v0.7.1 on 27 July, three weeks earlier. The advisory is complete for anyone upgrading normally. It is incomplete only for forks pinned to an older base that cherry-pick the single backport, which is a narrower and more interesting problem, and it is exactly what caught ZetaChain: its fork carried duplicate unexported copies of the helper functions, so patching the exported one left the live path untouched.

What the policy says, and what happened

Cosmos Labs publishes a disclosure policy. It is unambiguous about this situation:

When an issue presents an immediate or network-wide risk, Cosmos Labs will initiate emergency mitigations, private fix distribution, or coordinated upgrades before any public disclosure occurs.

By early August the team knew the flaw was network-wide and threatened funds in production. The policy language fits exactly. The patch was nonetheless shipped through the public release channel, and nothing went out privately until after the first chain had already been drained.

Their own explanation, quoted in full because it is not frivolous:

At this point, for a vulnerability that is known to threaten user funds in production networks, the team would typically use secure channels to distribute a patch privately to the affected networks. Because the patch had already been publicly available on the main branch without known exploitation, the team concluded that it would be safe to proceed with the silent patch process.

That is a real argument. The fix had been readable on main since 15 May, and nobody had exploited it in three months. The counter-argument is that a release is not a branch commit: the release notes flagged urgency and told operators to upgrade as soon as possible, while the changelog omitted what was being fixed. Signalling "this is urgent" while withholding "what" tells an attacker exactly where to look, and the resulting diff contains a panic string that names the bug.

Operators have been blunt. KiiChain published its own post-mortem five days before Cosmos Labs published theirs:

Publishing a security fix in the open, before the chains running that code have been told privately and given time to patch, hands the vulnerability to anyone reading the commit.

MANTRA made the operational argument rather than the moral one, and it is the more useful of the two:

Twenty hours was not a realistic window in which to assess, build, test and coordinate a state-breaking upgrade across 38 independent validators, particularly without a vulnerability-specific advisory.

I have not found a single operator publicly defending the handling.

Whether the patch was diffed is genuinely unproven

This is where the story gets over-told, so it is worth being careful. No forensics have been published establishing how the attacker learned of the bug.

Cosmos Labs points at the downstream disclosure, calling it "highly unusual" and noting it has released 37 vulnerabilities silently in the last 13 months without a downstream developer describing an exploit path publicly. That is fair as far as it goes. It does not answer why the release itself went out first. Culpability for the timing is genuinely shared. What is not shared, and what Cosmos Labs concedes, is that its own policy called for private distribution and it did not happen.

What was lost

Six chains, and the difference between drained and realised

ChainDrained on-chainPosition afterwards
MANTRA720.9 million OMFrom the burn address and a genesis multisig. No user balances altered, no rollback. Nothing recovered.
TAC2.99 billion TACBridged out, about 1.21 billion swapped for roughly 950,000 USDT. Around 1.66 billion still unsold.
KiiChain148.3 million KII over 18 iterationsAbout 64.6 million swapped for roughly 1.6 million USDT. Around 54% remains on chain and is recoverable.
NesaNot itemisedReported at about $60,000 by one outlet, and contested by another that reports no user losses.
Two further chainsNot disclosedCosmos Labs omits them for brevity, saying the attack pattern is consistent.
Nominal on-chain amounts and recovery status from the Cosmos Labs post-mortem of 28 August 2026. Cosmos Labs states the aggregate valuations were provided by the affected chains and have not been independently audited.

The aggregate is roughly $5.72 million realised: about $2.87 million sold on decentralised exchanges and an estimated $2.85 million on centralised ones, where accounts have been frozen pending investigation. Headline figures of $7.5 million and $9 million circulating for individual chains are nominal token valuations of the amount moved, not proceeds.

The advisory nobody's tooling could see

One finding that has had no coverage at all, and it is the most portable lesson here.

The advisory was never promoted to the global GitHub Advisory Database. It exists only as a repository-scoped advisory. A control check confirms this is an omission rather than a platform limitation: an earlier Cosmos EVM advisory from October 2025 is in the global database. The consequence is direct. Anyone relying on Dependabot, govulncheck or the OSV feed to tell them their dependency was vulnerable was never told. There is also no CVE, no CVSS vector and no CWE assignment on the record.

Take this with you

What to take from this if you ship or consume open-source dependencies

  • Treat a release that says important security fixes and does not say which as a disclosure event. If your vendor signals urgency without content, an attacker reads the same signal and has the diff.
  • Check whether your critical advisories reach the global GitHub Advisory Database, not just the repository. A repository-scoped advisory is invisible to Dependabot, govulncheck and OSV.
  • If you maintain a fork, diff the whole patched function rather than cherry-picking the named commit. ZetaChain found duplicate unexported helpers that left the live path unpatched after applying the official fix.
  • Write down in advance when you halt rather than upgrade. Cosmos Labs has now committed to publishing that standard, which is an admission it did not exist. Yours probably does not either.
  • Close the precondition where you can. Warden Protocol blocked permissionless vesting-account creation outright, and its own note says nothing depended on users being able to create them.
  • Do not let a silent-patch policy outlive the assessment that justified it. This patch was correctly silent in May and incorrectly silent in August, and nothing forced a re-review when the assessment changed.

The position

The technical bug is ordinary. An unchecked subtraction in a uint256 is the oldest arithmetic mistake in the field, and it was caught by a bounty researcher, fixed correctly, and audited by two firms that missed it. That is a normal week in software.

What is not ordinary is the sequence of judgement calls after the fix existed. A patch was withheld from release branches for three months because shipping it was inconvenient, then released publicly with an urgency flag and no content, then followed by a private warning that arrived after the money was gone. Each step was defensible on its own terms and the combination cost six chains about $5.7 million.

The lesson generalises past crypto, and this site keeps finding it in other clothes: an advisory whose thinness was the story, a CVE that arrived four weeks after the exploit. The failure mode is not the vulnerability. It is the information asymmetry between the people who can fix it and the people who have to, and it is created by disclosure choices rather than by attackers.

Sources

  1. PrimaryCosmos EVM GHSA-7g4w-cg88-2cq2 post-mortem, 28 August 2026Cosmos Labsaccessed 2026-08-30
  2. PrimaryBalance underflow in EVM StateDB, Critical, no CVE and no CVSS assignedCosmos Labs via GitHubaccessed 2026-08-30
  3. PrimaryBug bounty and disclosure policy, including the private fix distribution commitmentCosmos SDKaccessed 2026-08-30
  4. PrimaryPull request 1176, the SubBalance underflow guard, merged to main 15 May 2026Cosmos EVM on GitHubaccessed 2026-08-30
  5. PrimaryRelease v0.6.2, 19 August 2026, and its changelogCosmos EVM on GitHubaccessed 2026-08-30
  6. PrimaryCompare v0.6.1 to v0.6.2, confirming the backport commit dates of 19 August 2026GitHub APIaccessed 2026-08-30
  7. PrimaryPull request 40, publishing the vulnerability and exploitation path, 20 August 2026Push Chain on GitHubaccessed 2026-08-30
  8. PrimaryPull request 31, porting the fixes and identifying the unpatched duplicate helper pathZetaChain on GitHubaccessed 2026-08-30
  9. Reported byCosmos EVM Flaw Exploited After Cosmos Labs Knew Every Blockchain Running It Was Vulnerable, 29 August 2026The Hacker Newsaccessed 2026-08-30
  10. Reported byCosmos Labs says it wrongly cleared the bug behind a $5.7 million six-chain hack, 29 August 2026The Blockaccessed 2026-08-30
  11. Reported byCosmos Labs under fire over disclosure of bug affecting four blockchains, 25 August 2026Protosaccessed 2026-08-30

Share this briefing

Know someone who owns this problem? Send it to them.

Related briefings

The briefing, in your inbox

Practitioner analysis of cyber and AI security news. No vendor noise.

One email per briefing. Unsubscribe any time.