Crypto: a bug invented 49 million tokens that did not exist

Crypto: a bug invented 49 million tokens that didn't exist

On August 18, at around 17:30 Greenwich time, someone sent a transaction to a network called MAYAChain. Just one. It contained 23 instructions stacked one after another, and it chained together six different bugs like stringing beads.

Result: the protocol wrote in its accounts that it owed 49 million tokens to someone. Tokens that existed nowhere. It only had 168,000 in the till.

The rest played out in a few seconds.

An accounting ledger where a machine has recorded 49,000,000 tokens issued, next to an almost empty vault containing 168,000

The stamping machine went haywire. The vault didn't keep up.

First, what is this thing

Maya is what is called a cross-chain exchange protocol. The basic idea is pretty nice: let you exchange bitcoins directly for something else, without going through an exchange platform, so without entrusting your funds to a company that could shut down or get hacked.

For that to work, you need raw material. People deposit their tokens into shared reserves, called liquidity pools, and receive a share of the fees along the way. When you make an exchange, you draw from the pool and deposit what you give into it. That's the principle.

Let's be honest right away about the size: Maya is a small player. We're talking about a protocol whose pools altogether were worth around ten million dollars, not a giant. If you'd never heard of it, that's normal. What makes the affair interesting isn't the amount, it's the mechanics. It's magnificently stupid and it can happen to anyone, including in code that has nothing to do with crypto.

The safeguard that turned back on itself

Here's how it unfolded, as the team itself published in its incident report.

Four-step diagram, a sensor that triggers falsely, a calculation with no upper limit, a failed transfer whose entry remains, and a small deposit that takes over the whole pool

Four steps, a single transaction. Each box on its own is harmless.

One. The false alarm. Maya monitors its own fund movements. When money leaving it seems abnormal, it considers that there has been a theft. The attacker managed to make perfectly legitimate transfers look like missing funds.

Two. The uncapped compensation. When the protocol thinks it has been robbed, it automatically compensates those who had deposited into the pool concerned. It's a protection mechanism, written to be nice to users. Except the compensation calculation had no upper limit. No "above this amount, we stop and call a human". The team credited a pool with 49.45 million CACAO tokens, its home currency. The chosen pool wasn't there by chance: it was one of the smallest in the house.

Three. The transfer fails, but the line remains. The protocol then tries to actually transfer those 49 million from its reserve. It only has 168,000. The transfer fails, obviously. But an accounting error means that the inflated balance, for its part, remains recorded. The protocol now firmly believes that there are 49 million tokens in this pool.

It's this third step that fascinates me. The transfer failed. The system did see that it didn't have the money. And it kept the figure anyway.

One hundred tokens, and half the stock

The rest is almost a formality.

The pool now displays a fortune. The attacker deposits 100 tokens into it. Since the pool is tiny in real value but enormous on paper, this deposit gives him 99.93% ownership of the pool.

A very unbalanced scale, a single coin on one side, a mountain of coins on the other

One hundred tokens on one side. Almost half of the currency in circulation on the other. We've seen more modest returns on investment.

He then takes his share: 48.87 million CACAO tokens. Almost half of all that exists of this currency.

The whole thing, from the first action to the last, in the same transaction.

What it really cost

The figures come from the team's report, published on August 19, so these are their figures.

The attacker left with around $1.65 million in assets. Of that amount, $1.36 million moved to other chains, including 20.83 bitcoins sent to a single address, and roughly $291,000 remains locked up.

CACAO itself collapsed. It was worth around $0.115 before the attack, it fell as low as $0.013, before climbing back toward $0.03. This is not some mysterious market movement to interpret: half the currency was created out of thin air and then sold, we know the exact cause and the exact time.

The total value of Maya's pools fell by around $10.9 million, much more than what the attacker took, because the panic is joined by those pulling their money out and those coming to profit from the price gap.

The team's reaction: shutting down the network. They stopped trading to contain the damage, announcing that they would fix the flaw before reopening. It is the right decision and it deserves to be noted, because it costs dearly in reputation and it was made in an emergency.

The lesson, and it's not limited to crypto

That is why this story interests me much more than its amount.

The code that blew everything up was not attack code. It was a protective mechanism. Something written with good intentions, to reimburse users if the protocol were robbed. The patch was the hole.

And the technical flaw comes down to three words: no cap. A value calculated automatically, paid automatically, without anyone having written “and if this number exceeds an absurd value, we stop”.

Honestly, who has never written that? A counter that increments without a limit, a refund loop, a discount calculation that could become negative, a quantity field with no maximum. We all test the normal case. We test the case where the number goes haywire much less often, because we tell ourselves it will not go haywire.

The second lesson is even more universal: an accounting entry survived the failure of the operation it described. The transfer failed, the line remained. This is the kind of bug you find in any management application, and it is simply called a transaction that was not properly closed.

If you write code that handles balances, there are two questions to ask yourself today, and they take ten minutes: do my automatic calculations have an upper limit, and are my entries cancelled when the operation fails?

And if you hold crypto, how does it concern you?

Directly if you had deposited into a Maya pool, otherwise not at all. Bitcoin itself is not to blame for a single second: the blockchain worked exactly as planned, it carried the attacker's 20.83 bitcoins without batting an eyelid, because that is its job and it does not judge.

What gave way was software built on top of it. The distinction matters, because we often read “bitcoin was hacked” when what was hacked was a program that uses it.

What this does remind us, though, is that where you leave your funds is a technical decision. An automated protocol is not safer than a platform because it is automated: it is risky in a different way. The platform can shut down or get burgled. The automated protocol does exactly what its code says, including when its code says something stupid. There is nobody to say that 49 million, come on, is a lot.

I wrote three weeks ago about how 70 million dollars had left a hardware wallet without anyone touching a single vault, just because of a flaw in the way random numbers were generated. Same kind of lesson : in this field, money is almost no longer stolen, it is calculated.

What I think

What strikes me is the disproportion between the elegance of the attack and the banality of the flaw. Chaining six bugs in a single transaction, understanding that a compensation mechanism can be triggered on demand, targeting the smallest pool so that the leverage is maximal : there is real expertise in that, and quite a few hours spent reading code.

And on the other side, an if was missing.

The only truly positive point of the affair, and I note it because it is rare, is that the team published the complete sequence of events, bug by bug, instead of talking about a “security incident” in three vague lines. We know exactly what happened. This is how others avoid doing the same thing again, and this is precisely what we never get when a big company gets caught out.

So if you maintain code that sends money all by itself, go reread your refund function this weekend. Does it have a cap ? Sure ?


Sources

Join the conversation

You need an account to comment on this article. Creating one is free and takes under a minute.

  • The XMLTV file, free to download every day
  • Comment on articles and reply to other readers
  • Get an e-mail when an article you follow is updated

No comments yet.

Une erreur s'est produite. Cette application peut ne plus répondre jusqu'à ce qu'elle soit rechargée.Veuillez contacter l'auteur. Reload 🗙