How We Prevented the Biggest Hack in Crypto History

Cayden Cayden #bug-bounty#crypto

How Veria AI found an infinite mint in the XRP Ledger that put $94B at risk

Last month, our AI discovered a vulnerability that let anyone mint infinite XRP.

XRP is the fifth-largest cryptocurrency, bigger than Solana and USDC, with a $94B market cap. The XRP Ledger it runs on was created by Ripple in 2012, which makes it one of the oldest blockchains.

$94 Billion at Risk

XRP launched with a fixed supply of 100 billion tokens. You can’t mine or stake on the network, and each transaction burns a small fee, so the supply should only ever go down.

The bug we found let anyone create 18 trillion XRP with a single transaction, 184 times the total supply.

That put the full $94B at risk, more than 60 times the largest crypto hack on record (Bybit, $1.5B).

To our knowledge, no whitehat has ever found a bigger vulnerability. The previous record we know of was a 2021 bug in Polygon that put $24B at risk.

A Decade-Old Bug

The bug had been live on the XRP Ledger for nearly a decade, in code written in 2015 and 2017.

This is one of the most heavily reviewed codebases in crypto. The XRP Ledger has paid out well over $1M in bug bounties and has had more than a dozen audits and audit contests since 2024 alone, including one contest with a $550K prize pool.

We suspect one reason nobody caught it is that it chains together two bugs that are harmless on their own. The first lets a payment charge almost nothing while paying out trillions. The second is in the safety check meant to catch exactly that, and it breaks in the same way.

How Our AI Found It

We pointed our agent at rippled, the software that runs the XRP Ledger. It found both bugs, worked out how to chain them together, and built a working exploit on a local network to prove it.

It flagged the bug at 11 on a Monday. I didn’t believe it, so I read through the proof of concept three times.

By 4am I still couldn’t find anything wrong with it, so I woke up our founding engineer to go through it with me. Vulnerabilities directly affecting $94B don’t come along often, and neither of us could believe one had been sitting in the XRP Ledger for nearly a decade without anyone noticing.

By morning we were sure it was real.

The Veria agent hunts not only for the highest-impact bugs, but also for how lower-severity vulnerabilities can be chained into something much worse. After we disclosed this one, we pointed general-purpose coding agents directly at the vulnerable code, and they still couldn’t find it.

Patched in Three Days

We reported it through XRPL’s bug bounty program, and their team moved quickly.

  • Sept 22: reported and confirmed the same day
  • Sept 23: fix merged
  • Sept 25: fix released, with over 80% of validators upgraded

Normally, changes to how the XRP Ledger processes transactions go through an amendment process, where validators vote on the change over several weeks.

For the first time in the more than ten years since that process was introduced, XRPL skipped it and shipped an emergency fix. As their disclosure report put it, “an attacker could have created spendable XRP far beyond the total supply in a single validated transaction.”

RippleX found no evidence that the bug was ever exploited. We thank the XRPL team for their quick response and triage.

The Largest AI Bug Bounty

We were awarded the maximum $250,000 bounty offered by XRPL’s bug bounty program. To our knowledge, that’s the largest ever paid out for a vulnerability discovered entirely by an AI agent.

AI Cuts Both Ways

Earlier this summer, an attacker drained 1,367 BTC ($89M) from Coldcard wallets through a bug that had been sitting in the firmware for five years, and Coinkite suspects AI helped find it.

AI is making old bugs much cheaper to find, and attackers have access to the same tools you do. Even a codebase as heavily audited as the XRP Ledger had a critical vulnerability hiding in it for a decade.

Fortunately, there are far more good hackers than bad ones. This bug was found by people who reported it, and the XRPL team fixed it before anyone could use it. As long as defenders adopt these tools as fast as attackers do, the good guys have the upper hand.

The rest of this post is the technical deep dive: how the two overflows work, how they chain together, and how the fix closes them.

Technical Deep Dive

Background: XRP and the DEX

XRP is divisible into one million “drops,” much like lamports in Solana:

1 XRP=1,000,000 drops

All 100 billion XRP were created in the genesis ledger, and no transaction is supposed to create more. The open source rippled codebase implements the entire chain: transaction processing, consensus, ledger storage, and RPC interfaces.

XRPL also supports issued currencies called IOUs. An IOU represents an obligation from an issuer in a currency code. Anyone can issue one, and IOUs trade against XRP on XRPL’s built in decentralized exchange (DEX).

That DEX ends up being the attack path which turns attacker issued IOUs into an infinite XRP mint.

Background: Offer Processing and the BookStep Conversion

Cross-currency payments can consume offers from the DEX. Internally, the payment engine models each order-book conversion as a BookStep.

For example, a payment may spend XRP, consume offers selling an issued USD token, and deliver that USD to the destination. When several offers have the same quality (exchange rate), BookStep aggregates their inputs and outputs.

The implementation limits an individual BookStep traversal to 1,000 offers.

Background: Transaction Invariants

After a transaction is applied, rippled runs invariant checks before committing the result. If any check fails, the transaction’s changes are thrown away.

One of these checks is the XRPNotCreated invariant. It walks every ledger entry the transaction modified and adds up the net change in native XRP. For any valid transaction, that net change must be exactly the negative of the fee:

Σ(XRP after)−Σ(XRP before)=−fee

A positive result would indicate that a transaction created XRP, and it is rejected.

A separate invariant, XRPBalanceChecks, prevents an individual account from holding more than the original 100 billion XRP supply.

The BookStep aggregation code was written in November 2015, and the XRPNotCreated invariant in February 2017. To put that into perspective, Ethereum launched in July 2015 and Solana in March 2020.

Overflow #1: Integer Overflow in BookStep

When a payment crosses an order book, BookStep doesn’t settle each offer in isolation. As it walks the offers in a strand, it accumulates their inputs into a running multiset and collapses it into a single total.

boost::container::flat_multiset<TIn> savedIns;
// ...
savedIns.insert(stpAmt.in);
result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));

where result.in is the total amount the source account is ultimately charged.

Notice the sum() function. This is the helper function that BookStep uses to sum a collection of amounts:

template <class TCollection>
static auto
sum(TCollection const& col)
{
using TResult = std::decay_t<decltype(*col.begin())>;
if (col.empty())
return TResult{beast::kZero};
return std::accumulate(col.begin() + 1, col.end(), *col.begin());
};

When the collection contains XRPAmount values, std::accumulate calls XRPAmount::operator+=. The underlying value is a signed 64-bit integer:

using value_type = std::int64_t;

The addition does not check whether the result still fits within the signed 64-bit type.

This means that if a single BookStep traversal consumes enough offers whose combined XRP input exceeds the signed 64-bit range, the accumulated total wraps around to a small value.

The offers themselves are settled one at a time, inside consumeOffer, each at its own true input amount:

auto const dr = offer.send(
sb, book_.in.getIssuer(), offer.owner(),
toSTAmount(ofrAmt.in, book_.in), j_);

Since the aggregate amount is essentially ignored in this path, each offer owner is credited ofrAmt.in, the full unwrapped amount their offer asked for.

Signed integer overflow is undefined behavior in C++, so whether this crashes or silently wraps depends on how the binary was built. We tested this against the official release binary and confirmed that the overflow wraps silently, with no crash.*

*Notably, had rippled been built with overflow trapping (for example, -ftrapv), this bug would have become a node crash (DoS) instead.

Overflow #2: XRPNotCreated

Overflowing BookStep creates XRP out of thin air, but this should never survive to a committed ledger. The XRPNotCreated invariant exists precisely to catch it.

Unfortunately, its accumulator is also a signed 64-bit integer, and the invariant checks the accumulated total rather than individual state changes:

class XRPNotCreated
{
std::int64_t drops_ = 0;
};

As the invariant walks the modified entries, visitEntry subtracts each account’s balance before the transaction and adds its balance after:

case ltACCOUNT_ROOT:
drops_ -= (*before)[sfBalance].xrp().drops(); // before
// ...
case ltACCOUNT_ROOT:
drops_ += (*after)[sfBalance].xrp().drops(); // after

Once every entry has been visited, finalize checks that the net change is exactly the fee that was destroyed and nothing more:

if (drops_ > 0)
return false; // XRP was created
if (-drops_ != fee.drops())
return false; // net change doesn't match the fee

In a normal transaction this is fine. The only XRP that should disappear is the fee, so -drops_ must equal fee.drops(), and anything positive means XRP appeared from nowhere.

But drops_ accumulates the real balance changes. When a payment credits enough accounts that their gains sum past the signed 64-bit bound, drops_ overflows. Because the invariant operates over the entire transition, the true net change, an enormous positive number, folds back down to a negative value that looks like a fee burn.

So finalize sees a net change of roughly -fee and lets the transaction through.

Chaining the Two Primitives

BookStep by itself can mint XRP, but XRPNotCreated rejects any transaction that creates XRP, so the mint never commits. And the XRPNotCreated overflow by itself is unreachable.

The goal was to chain both primitives together. Conveniently, both have the same type of overflow: result.in in BookStep and drops_ in XRPNotCreated are both signed 64-bit integers.

result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts)); // BookStep
// ...
std::int64_t drops_ = 0; // XRPNotCreated

If an attacker can choose the credits so that their total lands on a multiple of 264, then both accumulators wrap by the same amount.

Constructing the Transaction

Three constraints shape the transaction:

  • Clean wrap: Both sums are modulo 264, so N offers of ∼2k drops with N a power of two can land N⋅2k exactly on a boundary.
  • Per account cap: Each maker is credited its real amount, bounded by 100 billion XRP (kInitialXrp 1017 drops ≈256.5), so each offer is at most 256 drops.
  • Strand limit: kMaxOffersToConsume caps one traversal at 1,000.

Putting these together gives 256 offers of 256+1 drops:

256×(256+1)=264+256 drops

The +1 keeps the total off exactly 264 (which wraps to 0), so it wraps to a usable 256 drops instead.

Hitting Both Primitives

The same 264+256 flows through each overflow:

// BookStep: The aggregate wraps to 256, but each offer still pays out in full
result.in = sum(savedIns); // 2^64 + 256 -> 256
offer.send(sb, book_.in.getIssuer(), offer.owner(),
toSTAmount(ofrAmt.in, book_.in), j_); // maker gets 2^56 + 1
// XRPNotCreated: +2^64+256 (makers) - 256 (source) - fee = 2^64 - fee -> -fee
if (-drops_ != fee.drops())
return false; // never trips: -drops_ wraps to exactly the fee

The buyer is charged 256 drops, the 256 makers collectively receive ∼264 drops of real XRP, and the invariant sees only a fee burn and commits the ledger.

In summary:

  1. Set up 256 offers: Issue a worthless token with 256 offers, each selling for 256+1 drops of XRP.
  2. BookStep overflows the charge: sum(savedIns) wraps 264+256 down to 256 drops.
  3. XRPNotCreated overflows the check: The net change of 264−fee wraps to −fee and passes.
  4. Ledger commits: The invariant passes, and the minted XRP is written to the XRP Ledger.

Impact

One successful trigger credits the maker accounts with:

264+256 drops=18,446,744,073,709.551872 XRP

After accounting for the source debit and fee (which are negligible next to the amount created), that is 18.45 trillion XRP spread equally across 256 accounts.

The 100 billion cap is still enforced per account. All 18.45 trillion XRP is spendable, but it has to stay spread across separate accounts so that no single account exceeds the cap.

The setup requires about 256 funded accounts, but it does not require the attacker to operate a validator or have any special access. Once submitted, it is processed as an ordinary signed transaction. Because the setup can be recreated with additional accounts, the attack is repeatable, so an attacker could mint an unbounded amount of native XRP, 18.45 trillion at a time.

An attacker could never have sold that much XRP at market price, so the real exposure is the value of every existing XRP: the full $94B market cap.*

*At XRP’s price of $1.59 at the time, 18.45 trillion XRP would have a notional value of roughly $29 trillion.

The Fix

The fix shipped in rippled v3.4.1:

  • Adds an overflow check when BookStep sums offer amounts, so a sum that would overflow now fails cleanly instead of minting anything.
  • Widens the XRPNotCreated accumulator so the net change can no longer wrap around to look like a fee burn.
  • Hardens balance summation in other code paths.

Timeline

  • 09/21/2026 - Veria AI is run on rippled and identifies the infinite mint
  • 09/22/2026 - Veria AI builds a working PoC of the infinite mint on localnet
  • 09/22/2026 - Cayden validates the PoC and confirms the bug is reachable on mainnet
  • 09/22/2026 - Infinite mint reported through XRPL’s bug bounty program and confirmed the same day
  • 09/23/2026 - Fix merged
  • 09/25/2026 - rippled v3.4.1 released as an emergency fix, with over 80% of validators upgraded
  • 10/08/2026 - Veria AI is awarded the maximum critical bounty of $250,000
  • 10/09/2026 - XRPL publishes its disclosure report

About Us

At Veria Labs, we build AI pentesting agents that automatically find and fix security vulnerabilities in your application. Founded by members of the #1 competitive hacking team in the U.S., we work with teams like Phantom, Tempo, and Provable to find bugs like this before attackers do.

Think we can help secure your systems? Get in touch.

PoC

All files related to the PoC can be found in this GitHub gist:

https://gist.github.com/SuperBeetleGamer000/131515ad1965a67c6f210aa7dedce67d