Skip to content
Dark abstract illustration of a machined inspection hall receding into depth, twenty-eight identical teal verification armatures clamped on brass tally plates in ranked bays, with a small red slip in a foreground guide channel whose retaining notch was never cut
exploitsAugust 18, 20263 min read

Coreum Bridge $200K Exploit: 17 Signers and One Missing Destination Check

Dmitry Serdyuk
Dmitry SerdyukCo-Founder & CDO

Updated on August 18, 2026

TL;DR

On August 9, 2026, an attacker drained the tx (formerly Coreum) XRPL bridge of 199,916.3 XRP across 94 payments in 87 minutes, leaving a reserve that had held roughly 200,410 XRP with about 493 XRP in it. No key was stolen. Every one of those payments was a valid 17-of-28 multisignature transaction the bridge's own relayers built, signed, and submitted on purpose. The relayer scanned the bridge account's transaction history for payments carrying a Coreum recipient memo and treated the memo as proof of deposit, without ever checking the payment was addressed to the bridge. Because the bridge issues wrapped tokens on the XRP Ledger and has rippling enabled, third-party payments it never received still land in that history. The attacker sent himself money, attached a memo, and the bridge paid. The signing worked exactly as designed, and that is the problem: a quorum of seventeen signers running one codebase is a quorum of one.


What Actually Left the Bridge?

The public figure is 199,916.3 XRP, worth a little over $200,000, drawn from a reserve that CCN reported falling from roughly 200,410 XRP to about 493.5. We pulled the bridge account's history directly from the XRP Ledger to check it, and the ledger agrees exactly.

Every on-chain number below comes from public XRP Ledger mainnet queries run on August 18, 2026: account_tx, account_info and account_objects against a public JSON-RPC server, cross-checked against XRPScan's public account API, which independently labels the account "Coreum Bridge." Anyone can reproduce them.

Between 19:26:30 and 20:53:50 UTC on August 9, the bridge account rxXXXeMX8Gy5YvibvGLnQJ1XKKD7UswM1 sent 94 XRP payments, every one tesSUCCESS, every one carrying exactly 17 signatures. They alternated with metronomic regularity between two destinations, 47 payments each: rwt8PJhyXWgW8uwmTjmQ7Rw89tJHELFgb5 and rfXSfH2q4zhGWdw45nYcWfjvFN5ZfwE1U6. The amounts sum to 199,916.3 XRP. Both wallets were created the same afternoon, at 17:34:52 and 17:57:40 UTC, the second funded by the first, and both sit at 2.999993 XRP today.

tx's technical lead Reza Bashash put the loss at 198,715.88 XRP, and crypto.news reported roughly 1,200 XRP of the gap as unexplained. The gap is 1,200.42 XRP. We checked whether an inbound payment could net it out, and there were none to the bridge at all during the window, so the lower figure is not a net-of-deposits number. Trust the ledger: 199,916.3 XRP left, gross.

There is also a second leg that no public report we found has counted.

Before touching XRP, the attacker ran the same bug against the bridge's own issued token. Between 19:16:42 and 19:23:11 the bridge sent nine payments of the currency coreum7c8eb29e07 to those two wallets, totalling 4,356,778.58 units. Coreum's own token registry identifies that currency code as ucore, the native TX token wrapped onto the XRP Ledger and issued by the bridge account itself. The shape of those nine is the tell: a 91.965 probe, a jump to 19,940.4, then six payments that double to the decimal place, 39,880.9 through 1,276,187.5. The ninth stops at 1,824,252.1, a 1.43x step where a doubling would have been 2,552,375. Something capped it there, and three minutes later the attacker switched to XRP.

That leg is worth less than it sounds, which is probably why it went unreported. Both attacker wallets still hold the bulk of it, 2,818,686.1 and 1,250,945.4 respectively, about 93 percent of everything issued. The XRPL order book for wrapped TX was too thin to exit into, so most of it is stranded. It is still a real liability: every unbacked wrapped token on the XRPL side is a claim against real TX held on the Coreum side, which sharpens what tx meant in acknowledging that bridged assets are not currently fully backed. The hole is not only the XRP.

The two legs together also explain a stray number. Reporting settled on "97 minutes" while the XRP drain took 87. Measure from the first forged credit at 19:16:42 to the last payment at 20:53:50 and you get 97 minutes and 8 seconds. The clock everyone is quoting starts at the wrapped-token probe.


How Does an XRPL Bridge Know a Deposit Happened?

A lock-and-mint bridge has one question to answer, over and over: did money actually arrive? Everything else is bookkeeping.

The tx bridge answers it with relayers. Per the bridge specification, each relayer watches the bridge's XRPL account and submits an evidence record to a CosmWasm contract on the Coreum side when it sees a deposit. The contract counts evidence from distinct trusted relayers and mints once enough agree. Withdrawals run the same machinery backwards: the contract queues an operation, relayers sign an XRPL payment, and the account's multisignature releases the funds.

The signing set is on-chain and anyone can read it. The bridge account carries a signer list of 28 entries, each with weight 1, and a threshold of 17. So 17 of 28, and the account record reports the same disableMasterKey and defaultRipple settings we read from the ledger.

Now the part that makes the XRPL different.

On the XRPL, a token is a balance on a trust line between a holder and an issuer, not an entry in contract storage. For two holders to pay each other in that token, the payment has to pass through the issuer's books. That indirect movement is called rippling, and the XRPL documentation defines it as "the indirect movement of funds that occurs for any payment of trust line tokens". An issuer opts in by enabling the DefaultRipple account flag, and the docs are blunt about why: "Issuers must enable this flag for their customers to be able to send tokens to one another."

The tx bridge issues wrapped Coreum-side tokens, so it needs that flag. We read it off the account: Flags is 9437184, which decomposes to lsfDefaultRipple plus lsfDisableMaster, and XRPScan's parsed view reports both set. Rippling on, master key disabled so only the signer list can move funds. Both are correct for a bridge.

That has a consequence. When a payment ripples through an issuer it touches that issuer's ledger entries, and the account_tx API returns "a list of validated transactions that involve a given account." Involve, not "were sent to." A cross-currency payment between two strangers, which the bridge neither sent nor received, lands in the bridge's feed because the bridge's trust lines moved.

That feed is not a deposit log. The relayer read it as one.

We checked how common that shape is by reading the bridge account's recent history, and it is everywhere. Ordinary XRPL arbitrage bots fire circular cross-currency payments to themselves all day, and dozens show up in the bridge's feed, some delivering plain XRP, none involving the bridge as sender or recipient. The relayer keyed on ambient market noise.


Where Exactly Was the Bug?

In the open-source relayer, and you can still read it.

The scanner is honest about what it does. AccountScanner.ScanTxs calls AccountTx against the configured bridge account and pushes every transaction it finds onto a channel. A history reader, claiming nothing more.

The processor is where the assumption goes wrong. In xrpl_to_coreum.go, every transaction off that channel hits a two-way sort:

if p.cfg.BridgeXRPLAddress == tx.GetBase().Account {
	return p.processOutgoingTx(ctx, tx)
}
 
return p.processIncomingTx(ctx, tx)

The only address compared is the sender. Any transaction the bridge itself did not originate falls through to the incoming branch. There is no third branch for the case that dominates the feed of a rippling issuer: a transaction that involves the bridge and is addressed to somebody else entirely.

processIncomingTx then applies its filters in order. Is it final. Did it succeed. Is it a Payment. Does a memo decode to a Coreum recipient. It reads tx.MetaData.DeliveredAmount, converts it, and submits evidence carrying the issuer, currency, amount, and recipient. Search that function for Destination and you will not find it. The comparison that should have been the first gate, paymentTx.Destination == p.cfg.BridgeXRPLAddress, is absent.

So the memo carried the entire load. It was the only field separating a deposit from the arbitrage traffic rippling through the account every minute, and the sender chooses it. DeliveredAmount made the forgery pay: on a cross-currency payment it reports what reached the actual destination, which was the attacker's own second wallet.

That is CWE-345, insufficient verification of data authenticity, in its purest form. The relayer confirmed a message existed and was well-formed. Whether that message described anything that had happened to the bridge went untested.

One caveat on the source. The public repository's most recent commit to master is dated September 5, 2025, eleven months before the exploit. We cannot confirm the deployed relayers ran that exact build; the rebrand may have moved development somewhere private. The published code contains the flaw as described.


The Attack, Step by Step

All timestamps UTC on August 9, 2026, read from the XRP Ledger.

#TimeAction
117:34:52Attacker wallet B (rfXSf...E1U6) receives its first funding payments.
217:57:40Wallet B funds wallet A (rwt8P...Fgb5), which sets a trust line to the bridge's wrapped token 12 seconds later.
319:16:42First forged credit lands. The bridge issues 91.965 wrapped TX to wallet A. The probe works.
419:17:50 to 19:23:11Eight more wrapped-token payments: a jump to 19,940.4, six exact doublings to 1,276,187.5, then a short final step to 1,824,252.1. Total 4,356,778.58.
519:26:30Switch to XRP. First payment of 3,249 XRP.
619:27:20 to 19:31:22Three more near-doublings: 6,491.7, then 12,983.4, then 25,908.6. The ceiling is found.
719:32:21 to 20:53:00Automation takes over. Payments 5 through 93, roughly 1,650 to 1,694 XRP each, one every 55 seconds, alternating wallets.
820:53:50Payment 94, the last one, at 520.3 XRP. The reserve is down to about 493.
9Hours afterOn-chain tracing reported by CCN and crypto.news has the XRP swapped to ETH via THORChain, then sent into Tornado Cash. Both wallets now sit at 2.999993 XRP.

Forging and payout ran as two loops against different chains, which is what the table flattens:

rendering diagram…

The escalation is textbook: probe with 92 tokens, double until it breaks, then settle into a steady rate that runs unattended for 81 minutes while the reserve empties to 0.2 percent.

One thing the ledger poses without answering. The attacker proved a 25,908.6 XRP payout would clear, then spent 81 minutes taking roughly 1,650 to 1,694 at a time, fifteen times slower than the ceiling he had just measured. Those amounts drift down in a smooth curve before flattening, which reads more like a figure derived from a moving quantity than a fixed cap, though the XRPL side alone cannot confirm that. Nothing public explains the throttle, or how the missing check was found, though the source it lives in has been public for years.


Why Did 17 Independent Signers All Approve It?

Because they were not independent in the way that number implies.

A 17-of-28 threshold defends against a minority of relayers being malicious or compromised. That is a real threat and the design handles it. What it cannot handle is all 28 being wrong together, which is what happens when they run identical software against identical public data. Every relayer read the same forged payment from the same ledger, applied the same missing check, and reached the same conclusion. Protos reported it plainly: the operators reached identical conclusions because they all executed the same flawed code.

Twenty-eight signatures on a wrong answer is still a wrong answer.

Worth being precise about how independent those operators were, because the lazy reading is that the decentralization was theatre. We looked up all 28 signer accounts: every one funded and active, tracing back to 17 distinct funding parents, activated across more than two years from March 2024 to July 2026. The largest group sharing a funder is seven, well short of the 17 needed to sign anything.

On custody the diversity is real, and that is what makes this worth studying. Twenty-eight separate key holders made no difference, because the question each answered independently was computed by the same program from the same bytes.

This is the shape of the Wormhole bridge exploit of February 2022, where an attacker minted 120,000 wETH by feeding the Solana program a spoofed instructions sysvar account that its signature verification never checked was genuine. Different chain, different primitive, same category: the bridge confirmed the form of an authorization without confirming the fact it claimed. We covered the same failure in a different costume in Harmony's cross-shard receipt replay, and two implementations disagreeing about a ledger in the Verus bridge parser divergence.

To tx's credit, the response was quick and specific. The bridge was halted, tx published an acknowledgment on August 11, and Bashash named the mechanism publicly, pointing at the interaction between relayer logic and DefaultRipple rather than hiding behind "a sophisticated attack." A complaint went to the FBI's IC3. That is better disclosure than most incidents this size get.

The uncomfortable part is what tx also confirmed: the bridge had been through multiple internal and third-party audits. An audit certifies the code it was pointed at. If the engagement covered the CosmWasm contract and the XRPL multisig configuration, both of which behaved correctly here, it could clear every line in scope and never open the Go file where the money was actually decided.


Why This Generalizes Beyond One Bridge

The reusable lesson is about what counts as evidence.

Every bridge relayer, oracle, and deposit-crediting service answers the same question, and there are two honest ways to do it. Verify state: read the balance and confirm the thing you needed is true. Or verify events: observe a message and reason about what it implies. State verification is boring and hard to fool. Event verification is convenient, and its correctness rests on how well you modelled which events reach your feed and who can put them there.

The tx relayer verified an event, in a feed whose membership rules it had modelled wrong. Rippling is not obscure, and the bridge had to deliberately enable DefaultRipple to function at all. The team knew the flag was set. Nobody joined that up with "so our transaction history contains payments we did not receive."

Watch for that pattern in your own systems: a chain-specific behaviour, individually well understood, quietly widening what your watcher can see. The XRPL documents rippling thoroughly. Nothing in that documentation was ever going to tell the tx team which of their own assumptions it broke.


What Operators Should Do

  1. Check the destination, not the memo. For any deposit-crediting path, the first gate is that funds arrived at your address. Memos, tags, calldata, and event payloads are attacker-controlled routing hints. Here the whole fix is one comparison in processIncomingTx before any evidence is built.

  2. Reconcile against balance, not against a transaction feed. Credit deposits from an observed increase in your own balance, and settle them against the reserve on a schedule. A relayer asking "did our balance go up by this amount" instead of "did a matching transaction appear" fails closed on the 92-token probe.

  3. Alarm on reserve depletion, on a timer measured in minutes. This drain ran 97 minutes at a constant rate and stopped only when the reserve was empty. A rule as crude as "reserve fell more than 10 percent in an hour" fires around the fifth payment, with 190,000 XRP still in the account. Monitoring would not have prevented this exploit. It would have capped it at a rounding error.

  4. Put the off-chain relayer in the audit scope, in writing. Bridge audits gravitate to the contract because that is where the tooling lives, but on a lock-and-mint bridge the deposit-detection code decides when money is real. Name the repositories and files in the statement of work.

  5. Model the correlation in your quorum. Write down what your threshold actually defends against. If every signer runs the same binary against the same data, the honest signer count for a logic bug is one. At minimum, have a second implementation reconcile totals and halt on disagreement.

  6. Cap withdrawals per address and per window. No legitimate flow needed 47 payments to one new wallet in 87 minutes. A per-address daily cap sized to real usage turns a total reserve loss into a bounded one.

  7. Enumerate what your chain's history endpoint really returns. Read the API contract for the exact word it uses. On the XRPL it says "involve." Then test that a third-party payment which merely touches your account credits nothing.


Frequently Asked Questions

How much did the Coreum bridge lose? 199,916.3 XRP, roughly $200,000, confirmed by summing the 94 outgoing payments on the ledger. tx's own figure of 198,715.88 XRP is 1,200.42 lower and unreconciled. Separately, 4,356,778.58 wrapped TX tokens were issued to the attacker through the same bug, most of which he has not sold.

Was a private key stolen? No. The bridge's XRPL account has its master key disabled and moves funds only through a 17-of-28 signer list. All 94 payments were properly authorized by that list. The relayers were tricked into deciding a withdrawal was owed, then signed it correctly.

What is rippling, and why did it matter? Rippling is how the XRP Ledger moves trust line tokens between two holders: the payment passes through the issuer's books. Issuers must enable the DefaultRipple flag for their tokens to be transferable at all. The bridge had it set, so payments between unrelated third parties appeared in its history and the relayer read them as deposits.

Are bridged assets on tx still backed? Not fully. tx has acknowledged the shortfall and said all remedies are being evaluated. Reporting since points toward the Foundation replenishing reserves, though we have seen no published plan with figures and dates.

Would an audit have caught this? A review that read the relayer's deposit path very likely would have; the missing comparison is visible on a careful read of one function. Audits covering only the CosmWasm contract and the XRPL configuration would not have, because both were correct. That is a scoping failure, and it is the common one on bridges.


Sources / References

The on-chain figures above were read from XRP Ledger mainnet via public account_tx, account_info and account_objects queries, reproducible against any full-history server.

Dmitry Serdyuk
Dmitry Serdyuk

Co-Founder & CDO

Full-Stack Operator | Building across security, AI, and digital infrastructure.