Services Approach Projects Research About Request Engagement
Security & Risk

Silent Patches: How a Public Fix Left Four Chains Exposed for 96 Days

A critical bug in Cosmos EVM was fixed on May 15, 2026 and published on GitHub the same week. It took 96 days to reach a version chains could install. Four chains were drained in the five days after it shipped. Nobody leaked anything, and there is still no CVE.

Intermediate Security 10 min read Sep 9, 2026

On May 15, 2026, a developer fixed a critical bug in Cosmos EVM, the software that allows dozens of blockchains to run Ethereum contracts. The fix was merged into the public repository the same day.

It took 96 days to reach a version that chain operators could actually install.

In the five days after it finally shipped, four chains were drained.

There was no zero-day, no insider, no sophisticated intrusion. The fix sat in the open for three months, and the people running the vulnerable software did not upgrade, because nothing told them they had to.

What the software does

When you run a blockchain, you must run a protocol that manages who owns what. In Cosmos EVM that ledger is called the StateDB. Every time a transaction moves tokens, the StateDB adds to one balance and subtracts from another.

Some accounts hold tokens on a schedule. You are given a million tokens, but they unlock over four years. These are vesting accounts, and they hold two separate amounts:

  • spendable, the part you can move today
  • locked, the part you own but cannot touch yet

You are allowed to stake locked tokens. Locked tokens can be delegated to a validator and earn rewards even though you cannot spend them.

The staking system knows about both balances. The StateDB only tracked the spendable one.

The mistake, in three steps

Step one. An account has 1,000 spendable tokens and 5,000 locked. The account delegates 1,500 tokens to a validator.

Step two. The StateDB writes the result. It subtracts the full 1,500 from the balance it knows about, which is the spendable 1,000. In ordinary arithmetic that gives you −500.

Step three. These balances are stored as uint256, an unsigned 256-bit integer, meaning it has no way to represent a negative number. When a subtraction goes below zero it does not error and it does not go negative. It wraps around to the top of the range.

So 1000 - 1500 does not give you −500. It gives you:

output
115792089237316195423570985008687907853269984665640564039457584007913129639436

That is a 78-digit number. For scale, a token with a one billion supply and 18 decimals has 1.0 × 10²⁷ units in existence. The wrapped balance is about 10⁵⁰ times larger than that entire supply.

Before the patch, nothing checked for this. The code performed the arithmetic and wrote down whatever came out.

How a wrong number became stolen money

An enormous balance on its own is just a wrong number in a database. Turning it into someone else's tokens took a second step.

The attacker transferred from the inflated account to a second account they controlled. The receiving side added that gigantic figure to the recipient's balance, which pushed _that_ number past the top of the range and wrapped it back around to something normal-looking.

The result was an account holding a real, spendable, ordinary-looking balance that had been created out of nothing, and a victim account reduced to zero.

From there it was a normal withdrawal. The chain saw valid transactions signed by the right keys, because at the accounting layer everything added up.

The patch is one guard: before subtracting, check that the balance is actually large enough. If not, fail the transaction instead of wrapping.

This class of bug is not supposed to survive in 2026. Solidity 0.8, released in 2020, added automatic overflow and underflow checks to every arithmetic operation in a smart contract. If you write this mistake in a contract today, it reverts.

But this was not in a contract. It was in the software that _runs_ contracts, written in Go, where a uint256 subtraction does precisely what you tell it and nothing more. The safety net that catches this everywhere else does not extend one layer down.

The 96 days

The fix was public on May 15, 2026. It reached release branches on August 19, 2026. Between those dates, every chain running Cosmos EVM was exposed and there was no version they could install to protect themselves.

The pull request was titled:

output
fix: harden statedb balance and event amount handling

That title is accurate. It is also indistinguishable from routine cleanup. The word "security" does not appear. There is no severity rating, no advisory link, no CVE.

A patch is a description of the vulnerability. It shows exactly which check was missing and exactly where. Anyone who reads that diff can work backward to what happens without the guard.

That is why security fixes are normally handled differently from ordinary ones. The standard practice is to compress the window: build the fix privately, coordinate with the people running the software, release everywhere at once, and publish the advisory at the same moment.

What happened here was the reverse. The fix went public first and shipped 96 days later.

For three months, the vulnerability was documented in a public repository by the people who understood it best, and the only group who could act on that documentation was attackers. Operators could read the same commit and still had nothing to install.

The release that did not say what it fixed

On August 19 the fix finally shipped in v0.6.2 and v0.7.2. The release notes say:

output
This release contains important security fixes. We recommend all chains upgrade to this patch release as soon as possible using a coordinated upgrade.

That is a genuine warning and it deserves credit.

It is also the only signal. The "What's Changed" list on that same release names exactly one pull request: #1255, which changes some tests to use a different type of account as the deployer. The security backport, #1253, is not listed.

So an operator opening that page saw an urgent-sounding warning attached to a changelog that appeared to contain a test refactor. Upgrading a live chain requires coordinating every validator to restart at an agreed block. It is disruptive, and it does not happen because a release note is vaguely worried.

MANTRA was drained the next day.

The advisory that actually named the bug, rated it Critical, and listed affected versions was published on August 28, nine days after the release and eight days after the first chain was drained. By then all four were gone.

There is still no CVE. If your monitoring watches CVE feeds rather than GitHub Security Advisories, this vulnerability has never appeared in it at all.

What it cost

chaintakenwhat happened
MANTRA720,000,000 MANTRA, about $3.6Mchain halted within 14 minutes
TAC2,985,651,403 TAC, about $7.5Mbulk bridged to BNB Chain in 95 seconds
KiiChain148,326,583 KII, about $9.7M, across 18 walletshalted several hours in; 54.4% frozen, 45.6% bridged
Nesa257,703,733 NES, about 25.8% of supplyhalted August 24 and still offline two weeks later

Put MANTRA and TAC side by side.

MANTRA halted its entire blockchain in 14 minutes. For a decentralized network that requires validators to coordinate a shutdown, that is a genuinely fast institutional response. It was still too slow. By the time the chain stopped, 94.7% of the stolen funds had already reached an exchange deposit address.

TAC halted in about four hours. The money crossed to BNB Chain in 95 seconds.

Halting the chain is the emergency brake that operators point to when asked what happens if something goes badly wrong. In both cases it worked exactly as designed and arrived after the money was gone.

Nesa is the extreme version. It halted on August 24 and was still offline two weeks later. A chain can stop completely, stay stopped, and the tokens are already on Ethereum.

One number from Nesa is worth carrying away for a different reason. The attacker bridged NES worth roughly $50 million at the pre-exploit price and realized about $237,000 selling it, because dumping a quarter of a token's supply collapses the price you are selling into. KiiChain is the same shape: about $9.7 million taken, roughly $1.6 million actually realized. The damage to holders is the full number. The attacker's profit is not, and the gap between the two is why "amount stolen" headlines and "amount gained" are different questions.

"We can halt the chain" is frequently offered as a reason a risk is contained. A halt stops damage that has not happened yet. It does nothing about a transaction that already confirmed, and nothing at all once the proceeds have crossed a bridge to a chain your validators do not control.

What an operator can actually do

The bug was reported through a bug bounty program, credited to the researcher who found it, and fixed within two days of the pull request opening. That part of the process worked.

What failed was everything between the fix and a running validator. Four things follow.

Watch release branches, not the main branch. A fix merged to main protects nobody. The question that matters is which release contains it and when that release ships. Here the answer was 96 days.

Read a security warning with no matching changelog as urgent, not vague. When release notes say "important security fixes" and the change list shows something unrelated, that mismatch is itself the signal. It means details are being withheld deliberately, which means the upgrade is not optional.

Subscribe to GitHub Security Advisories, not just CVE feeds. GHSA-7g4w-cg88-2cq2 has no CVE. Tooling built around CVE identifiers never saw it and still does not.

Know what a halt actually buys you. If your incident plan is "halt the chain," time the path from detection to a stopped network and compare it against how long it takes to move funds across a bridge. TAC's answer was 95 seconds. MANTRA's best possible response was 14 minutes and it was not enough.

Securing the unseen

Two facts about this incident are uncomfortable next to each other.

The vulnerability was found responsibly, reported through the proper channel, fixed correctly in two days, and credited publicly. The hard technical work was done, and done well.

Four chains were drained anyway.

A correct fix that has not reached the software you are running is, from where you stand, identical to no fix at all. The code was safe on May 15. What failed was every step between that commit and a validator restarting.

Learn more at 0xhades.io/research

Sources

9 references
  1. cosmos/evm, PR #1176: fix: harden statedb balance and event amount handling The original fix against main, opened May 13, 2026 and merged May 15, 2026. The description states that it guards StateDB balance subtraction against underflow. github.com
  2. cosmos/evm, PR #1253: backport to release/v0.6.x The backport to the v0.6.x release branch, merged August 19, 2026. github.com
  3. cosmos/evm, PR #1254: backport to release/v0.7.x The backport to the v0.7.x release branch, merged August 19, 2026. github.com
  4. cosmos/evm, release v0.7.2 Published August 19, 2026. Contains the security warning quoted above, and a "What's Changed" section listing only PR #1255, a change to which account type the tests use as deployer. github.com
  5. GHSA-7g4w-cg88-2cq2, Balance underflow in EVM StateDB Published August 28, 2026, severity Critical, no CVE assigned. Affects versions below 0.6.2 and 0.7.0 through 0.7.1. Describes the vesting account and staking precompile path, the wrap to approximately 2^256, and credits the report to @AshmitSh4rma and other researchers through the Cosmos bug bounty program. github.com
  6. rekt.news, TAC The TAC loss of 2,985,651,403 TAC, the bridge to BNB Chain within 95 seconds, the chain halt roughly four hours after the exploit began, the KiiChain figure of 148,326,583 KII across 18 withdrawals with 54.4% frozen, and the exploitation dates of August 20, 22 and 24. rekt.news
  7. rekt.news, Mantra The MANTRA loss of 720 million MANTRA, about $3.6 million, the chain halt within 14 minutes, and 94.7% of funds reaching an exchange deposit address before that halt. rekt.news
  8. rekt.news, Nesa The Nesa exploit of August 24, 2026: 257,703,733 NES bridged to Ethereum via Hyperlane in a single transaction, about 25.8% of stated supply and roughly $50 million at the pre-exploit price, against approximately $237,208 actually realized after selling 185,744,335 NES across 241 fills as the price collapsed from $0.0125 to $0.00032. Nesa halted on August 24 and remained offline as of September 8. rekt.news
  9. rekt.news, KiiChain The KiiChain exploit of August 22, 2026: 148,326,583.15 KII, about $9.7 million pre-exploit, taken across 18 wallets. The chain halted at block 9,355,723 several hours after the attack began, freezing 80,728,575.06 KII (54.4%) while 45.6% had already crossed to BNB Chain, of which roughly 64.6 million KII was liquidated for about $1.6 million. rekt.news