Stratos (stratos-1) Exploit and Chain Halt: Post-Mortem

As of 2026-10-14. All figures read on-chain with stchaind against public stratos-1 RPC nodes, and with Ethereum JSON-RPC for the bridge side.

Summary

An attacker stole 83,616,206 STOS from stratos-1 in two drains through an unauthenticated EVM precompile. Then they halted the chain themselves. They bonded 13.5M STOS to a validator that had been abandoned and jailed since 2023, and forged its unjail. That validator now holds 66.18% of voting power with no node behind it, so the remaining 33.82% can never reach the 2/3 quorum. The last block is 16,008,475 (2026-10-13 05:21:19 UTC).

No private keys were stolen. The chain executed messages in other accounts' names without checking ownership.

Timeline

All times UTC. Stratos blocks unless marked ETH.

Time Block Event
2026-10-11 17:15:37 15,985,794 25 STOS test transfer comes in over IBC to the collector
2026-10-11 17:43:08 15,986,082 Collector funds the operator account st13v7q… with 1 STOS for gas
2026-10-11 17:55:37 15,986,213 Collector issues an authz MsgGrant to the operator
2026-10-11 17:55:43 15,986,214 Drain 1: one MsgExec with 11 forged MsgSends, totalling 50,616,206 STOS
2026-10-11 18:00:00 15,986,259 Collector deposits 10M STOS into the WSTOS bridge
2026-10-11 18:08:23 ETH 26,170,904 Bridge mints 10M STOS tokens on Ethereum to 0x57e12f…
2026-10-11 18:09:59 ETH 26,170,912 10M sold through router 0x52b335fd… for ~2.84 ETH
2026-10-12 01:39:46 15,991,081 MsgTransfer of 40,000,000 STOS on channel-1 to osmo14qpeg…
2026-10-12 01:52:43 15,991,217 10,000,024.6 STOS comes back from Osmosis to the collector
2026-10-13 05:02:18 16,008,276 Drain 2: forged MsgSend of 32,999,999 STOS from the WSTOS bridge contract to the collector
2026-10-13 05:02–05:07 16,008,280–333 Collector deposits 3 × 10M STOS into the WSTOS bridge
2026-10-13 05:03–05:08 ETH 26,181,354–382 3 × 10M STOS minted on Ethereum and sold through router 0x23617e59… for ~0.046 ETH in total
2026-10-13 05:11:34 16,008,373 Collector delegates 13,516,253.8 STOS to jailed validator jxd02. No consensus effect yet.
2026-10-13 05:21:14 16,008,474 Forged MsgUnjail for jxd02 goes through the precompile. EndBlock gives jxd02 13,516,254,828 voting power, effective from block 16,008,476.
2026-10-13 05:21:19 16,008,475 Last block, signed by 5 of the 8 previously active validators

Root cause

The address 0x0000000000000000000077656233636f736d6f73 (its bytes spell "web3cosmos" in ASCII) is a Stratos-specific feature added in v0.12.0 (stratos-chain, x/evm/types/msg_cosmos.go). An Ethereum-style transaction sent to it carries a Cosmos message, which the chain then executes as the account named inside that message.

The code does contain an ownership check. MsgCosmosData.ValidateBasic rejects the message unless its signer equals the Ethereum transaction's sender. That check is wired up in only two places, and both can be bypassed:

  1. The ante handler (app/ante/eth.go, EthTxCosmosMsgVerifierDecorator) is the gatekeeper that inspects a transaction before execution. It only looks at the transaction's top-level message.
  2. The execution path (x/evm/keeper/state_transition.go, RunSdkMsgFn) repeats the check only in simulation (if simulate { … }). Real execution relies on the ante handler having done it.

The authz module is enabled on stratos-1, and nothing on the chain blocks a MsgEthereumTx from being wrapped inside an authz MsgExec. Wrapped that way, the Ethereum transaction is no longer the top-level message, so the ante check never runs, and execution skips the check as designed. The message then runs as whichever account it names. Bank sends, staking, slashing and any other message type can be forged this way.

Timeline in the code: the feature was added in March 2024, released in v0.12.0 (tagged 2024-05-24) and activated on-chain by the v012 upgrade at block 3,543,000 (2024-05-28). It stayed exploitable for about 2 years and 4 months, until the first drain on 2026-10-11.

On-chain evidence:

The fix: enforce the signer check in the execution path itself, not only in the ante handler. Also block MsgEthereumTx inside authz MsgExec with an authz-limiter ante decorator, as other Ethermint-based chains do, and accept only an allowlist of message types. Security checks that live only in the ante handler, and that authz nesting can therefore skip, are a known pitfall on Ethermint-based chains. The August 2026 incidents on MANTRA, KiiChain and TAC were a different bug: an accounting underflow in the standard Cosmos EVM staking precompile.

Impact and fund flows

Victims

Victim Block Drained (STOS)
st19fc4wfj3xfpzqrx6jakg80qd6q8ffkcr4k382m (WSTOS bridge 0x2a7157…) 16,008,276 32,999,999.17
st1qrxg46rev37xknylc3v9wj3dmm3mwhqxt2q9ut 15,986,214 22,999,999.00
st1mpsgkhnx4t97zhrdse0ermde8j32cs3x7xj6x8 15,986,214 6,999,999.00
st1y0ujk7aqvuxt6t058gn6k2kq8f3pe96vzwknau 15,986,214 3,977,560.00
st1p5rs093e2te0hfva6phjksj6eeqtfyh7932yfh 15,986,214 2,313,533.78
st1ztngz8zmdl3tzz9xjf86tjtvkup0tc04q5h6vm 15,986,214 2,255,120.45
st1g7p0a3fwrevn9zxehpw52feps47g7l4mufwvg2 15,986,214 2,069,999.00
st1ervxwh7mnwkgapyr4rvfh4dggumsllhzlt7ejr 15,986,214 1,999,999.00
st1mj5er6s8yca9g4p4mmwsys8x8jvw6m72xnlfcx 15,986,214 1,999,999.00
st1wk0g99nmts0kfeyscxg2tpu9vagefutwp3trt0 15,986,214 1,999,999.00
st1x5nh4mz83zhtxgky7s6fhjzvxn20gsj9pkl6mn 15,986,214 1,999,999.00
st1yy5dt87lea3nqxngpgjvjz36huaq2q7m9xpy05 15,986,214 1,999,999.00
Total 83,616,206.33

The attacker left each drain-1 victim exactly 1 STOS. Most drain-1 wallets were not individual community holders. They were part of the team's split of the migrated ~70M into multiple wallets.

Drain 1 therefore mostly hit project-structured funds. Drain 2 hit the bridge reserve, which backs users' bridged tokens.

Collector ledger (st12lsj78… / 0x57e12f…)

The collector's flows reconcile to its current balance to within about 25 STOS of fees.

Flow STOS Where it is now
In: drain 1 +50,616,206.2 —
In: drain 2 (WSTOS bridge) +32,999,999.2 —
In: back from Osmosis +10,000,024.6 —
Out: IBC to osmo14qpeg… −40,000,000 Off-chain; about 30M net, not traced further
Out: 4 deposits into the WSTOS bridge −40,000,000 Minted on Ethereum and sold for ~2.88 ETH
Out: delegation to jxd02 −13,516,253.8 Bonded on stratos-1, frozen
Collector balance now 99,999.9 Frozen

Ethereum cash-out

The bridge contract on Ethereum (0xa04b06ee86130c471d0c275403ad5516d7fff91e) minted all four 10M deposits as STOS ERC-20 tokens (0x08c32b0726c5684024ea6e141c50ade9690bbdcc) to the attacker's address. The first sale emptied the STOS liquidity on Uniswap:

Sale Ethereum block Proceeds
10M via 0x52b335fd… (sent by 0x5f8d74fc…, probably a gasless-swap relayer) 26,170,912 ~2.84 ETH
10M via 0x23617e59… 26,181,356 ~0.0281 ETH
10M via 0x23617e59… 26,181,366 ~0.0130 ETH
10M via 0x23617e59… 26,181,382 ~0.0047 ETH

The total is about 2.88 ETH, roughly $7,200 at Etherscan's displayed ETH price. The attacker's Ethereum address now holds 0 STOS. Where the ETH went afterwards has not been traced. First mint: 0x162473ed….

Supply and bridge backing

STOS started as a 100M ERC-20 on Ethereum. stratos-1 launched on 2023-09-08 with all 100M STOS created natively in genesis, and native supply is still exactly 100,000,000 STOS with 0% inflation. The attack moved existing STOS and created none on stratos-1.

Each STOS token on Ethereum should be backed by native STOS locked on stratos-1. Normal bridge use moves tokens and reserve together, so the gap between Ethereum supply and the bridge reserve stays fixed:

Ethereum STOS supply Bridge reserve Gap
Expected after migration and seeding ~30M 7.54M ~22.6M
Just before the attack ~45.4M ~25.8M ~19.6M
Now 85.44M 32.85M ~52.6M

The attack added 40M tokens on Ethereum but only 7M net to the reserve (33M stolen, 40M deposited). About 3M of the pre-attack gap is unexplained.

Halt mechanism

The attacker turned a dormant validator into a 66.18% holder of voting power with no node behind it. Blocks need signatures from more than 2/3 of voting power, so block 16,008,476 can never be finalized. More than 1/3 is enough to halt a chain. With 66.18%, the attacker is far past that, though just short of the 2/3 needed to produce blocks alone.

Consensus set from block 16,008,476 Voting power Share
jxd02 (offline, consensus key SJ6kQ0Yso…) 13,516,254,828 66.18%
The 8 previously active validators 6,908,371,051 33.82%
Total 20,424,625,879 100%

Steps:

  1. jxd02 was jailed, so the 13.5M delegation at block 16,008,373 changed nothing in consensus.
  2. The forged unjail at block 16,008,474 emitted a validator update.
  3. CometBFT locked the new set into block 16,008,475's header (next_validators_hash).
  4. The network itself was healthy: 5 of the 8 old validators signed that last block.

The chain cannot be fixed on-chain, even with a patched binary. Every node checks signatures for block 16,008,476 against the locked set, and jxd02 never signs. Removing jxd02 would itself need a block.

About jxd02

jxd02 (stvaloper1qr0t2pcut8etqf4k7qlnclyf9zjn5yt7yw2grl) was not created by the attacker. It was one of ten throwaway validators, jxd01 to jxd10:

A validator's IP address is not recorded on-chain, and no live peer announces a jxd moniker. Whoever holds jxd02's 2023 consensus key could restart the chain by running a node with it, and the exploit would still be live.

Attacker cluster

Role Address State now
Collector st12lsj782pkldwh9ckmf6c7pd7cf3kmt4zh9tv3z / 0x57e12f1d41b7daeb9716da758f05bec2636daea2 99,999.9 STOS on stratos-1, 0 STOS on Ethereum
Authz operator (drain 1) st13v7qxgvv9ncs6t25jc5kqku4wxq8d6wyvtv66t 0.9 STOS, one transaction ever
Osmosis landing osmo14qpeg04hyx6s6dax44q9sx268ckgr6zzhhswja Not traced
Hijacked validator stvaloper1qr0t2pcut8etqf4k7qlnclyf9zjn5yt7yw2grl (jxd02) 13,516,254 STOS bonded, offline

Drain 2 and the unjail used the collector itself as the MsgExec grantee. Drain 1 used the operator account, which the collector had funded and granted one block earlier.

What a restart would take

The Stratos core team is no longer active, so any restart would have to be led by the validators and the community.

  1. Patch the binary (currently stchain v0.12.1) to disable the web3cosmos precompile entirely.
  2. Export state at height 16,008,475 (stchaind export --height 16008475).
  3. Edit the genesis:
  4. Verify and start: every validator reproduces the same genesis hash, then all start together.

Other issues: