Contents

Liquid Network Paid Out 3,996 Bitcoin for Coins That Never Existed

 

Ethical Hacking Complete Course Zero to Expert

Hack like black hat hackers. Penetration testing, Kali Linux, WiFi and web hacking, and the hacker mindset behind it.

→ Take the full course
 
Contents

An attacker emptied a vault of 3,996 bitcoin and then asked the owners to contact him. He wrote the message into the blockchain, where the rest of us could read along. What happened next was not a ransom.

On Sunday a payment left the bitcoin wallet that the company behind Liquid keeps as the backing for its own coin. 3,996 bitcoin went out, around 320 million dollars, and 197 stayed behind. Eleven of the fifteen companies that guard that wallet had put their signature under it, and each signature was correct. As far as the software could tell, a customer had walked up to the counter and asked for his money.

He was no customer, and the coins he handed in did not exist. He had made them out of nothing a little over half an hour earlier, and the software could not tell them apart from the ones that do.

Four hours later he did something you rarely see. He wrote a note to the people he had just taken it from. He put it in the bitcoin blockchain itself, where it stays for good:

1
we are whitehats. contact us on chain

An hour later, in a later block, an answer came back:

1
Please contact security@blockstream.com

That is how the two of them ended up negotiating in public, one block at a time. The rest of us read over their shoulders.

You can write a small piece of text into a bitcoin transaction. It goes in a field called OP_RETURN, it costs a few cents, and it stays in the chain for as long as bitcoin exists. It gets used for timestamps and short notes. These two used it to talk, because neither of them had another way to reach somebody they could be sure of.

He did not ask for money. He asked for something else:

1
Please fix the bug first. The chain is under risk at latest commit right now.

A man holding 320 million dollars, telling the people he took it from to hurry up and close the hole before somebody else came through it.

Then it got complicated, because other people were reading along, and some of them saw their chance.

On Monday morning two messages landed in the same minute. This was one of them:

1
Bridge nodes are patched, safe to return the funds.

And this was the other:

1
Thank you for finding the bug, we are working on a fix immediately, please send 3900 Bitcoin back to this address, you may keep the 98 BTC as Bounty reward.

Only the first one came from the company. The second was somebody hoping to catch the money on its way back by sounding official at the right moment, and the 98 they dangled was chosen with care. It is about the size of a finder’s fee. Big enough to tempt, small enough not to raise an eyebrow.

That afternoon a third one turned up:

1
Please do not send the coins back to bc1qdlld6...suhwxxr, instead we have a clean route address bc1q2fqggs624lhf3tcxwk7ankpl87gynmskklrw7t. You may keep 98 bitcoins for finding the bug.

Also not from the company.

He ignored both, and the dull precaution is what made that possible. The company signed its messages with the PGP key published on its own website. That signature carries the same fingerprint as the one on their site. You cannot forge that without the key, and neither of the two fakes carried a signature at all. The address he sent it back to was the one he had put in writing himself the night before, and they had signed off on it. Later that day he asked them to confirm it one more time.

That fake address has received nothing at all. Zero transactions, zero bitcoin.

That afternoon he sent it. One transaction, two payments, nothing else in it:

  • โ†’ 3,400 bitcoin back to the wallet he took it from
  • โ†’ 598.49955894 bitcoin to an address he kept

598 bitcoin is around 47 million dollars. It works out at 15 percent of what he took. That is roughly what a bug bounty pays in this corner of the industry, on the days it pays anything. In public no bounty was ever offered and no agreement has been published. He kept the 598 and sent the rest back.

The sidechain is still down. The bridge nodes are off and the exchanges got a request to freeze deposits and withdrawals. The chain also split during the pause, so that has to be cleaned up before anything restarts. Samson Mow, who runs JAN3 and used to work at the company, said on Monday that more fixes and security work are going in first. The company itself has said nothing in public about the missing 598. The stablecoins and the other assets on the sidechain were not touched.

By Monday evening the address was in the reporting, and the messages started arriving. More than two hundred notes sit in there, and the count is still climbing.

Many of them ask:

  • โ†’ “Dude just give me 10BTC and let me focus on living instead of grinding this 9-5 bs”
  • โ†’ “right now 10BTC is pennies to you yet everything to me”
  • โ†’ “Need 10BTC to save mi familia from Penia, please ser, mi familia!”
  • โ†’ “Wish for 0.1 BTC to kick-off my crypto journey, appreciate any support.”
  • โ†’ “I’d let you kick me in deez nuts for 10 BTC :)))”
  • โ†’ “smart enough to send this message, not smart enough to escape my 9-5… Grant me 10BTC and I’ll name my first born whatever you tell me to!”

One is from a man with a baby:

  • โ†’ “If you truly are white hat and return this, you’ll not only have saved my family that includes a few month old baby, but you will go down in history as a legitimate hero and legend.”

Some are kind:

  • โ†’ “God bless you for being merciful. Do good in the world with your reward.”

And some are business. The same memecoin advert copied dozens of times, launched on the back of the story while it was still running. A doubling scam pointing at a fake giveaway site. Several people explaining, unprompted, how he ought to launder it: into ETH through a set of bridges and then a mixing pool, or into Monero in small amounts. A run of accusations goes through it as well, naming company executives and calling the incident staged. Nothing supports any of that, so those names stay out of this.

A few hours after sending the 3,400 back, he wrote two characters into a bitcoin block:

1
:(

He has not said why. It sits in a block now, and it is not coming out.

He is still around. This morning he swept up the small transactions people had been sending him, posted another encrypted message, and paid a few thousand satoshi to carry it. The 598 bitcoin have not left his wallet.

Making around 4,000 coins out of nothing took him two things: a gap in how the software remembers its own work, and a weekend of patience.

On Liquid the amounts in a transaction are hidden. That is the point of it: a company shifting coins does not want the rest of the market watching. A node still has to be sure that a hidden amount is a sane number and not something negative or made up. It checks that with a range proof, a piece of mathematics that shows a hidden value sits inside a valid range without showing the value.

Checking one costs processing time, and a node checks a lot of them. So it remembers the answers. Verify a proof once, store the result, skip the work when the same proof comes past again.

The stored answer was filed under two things: the proof, and the commitment to the amount. It did not include which coin the proof was about. It did not include where the money was going.

That is the hole. Get one proof approved, and the node hands you that same stored answer later for a different coin heading somewhere else.

So he spent Saturday into Sunday planting 68 copies of the same proof across 92 transactions. Fourteen hours of quiet work, filling the memory of the nodes that would later have to check what he handed in. Then he made around 4,000 coins with nothing behind them and took them to a trading desk that swaps them for bitcoin. The desk did its job. It burned the tokens and asked the guards to pay out. The guards checked the request, eleven of them signed, and the money went.

That desk was not hacked and its key was not stolen. The desk said so and it is right. The cryptography did what it was told. It was told the wrong thing.

One more thing.

The repair for this bug already existed. An engineer at the company wrote it in early August. It went into the public source code on the first of September, five days before he walked in. Two files, one function, two extra parameters so that the stored answer is filed under the coin and the destination as well. Read those few lines and you can see what was missing before them.

It did not arrive with a warning on it. The fix went in as the last of ten commits in a pull request titled “blind/blindpsbt fixes”. Its own description calls it “small issues picked up during LLM scans”. A machine found this one, and it went in as housekeeping. Read the title of that pull request and you learn nothing at all. Read the titles of the commits inside it and you learn where the money is.

The trouble is that a fix in the source code is not a fix on the machines that are running. The newest released version of that software came out on 13 April. Nothing has been released since. So the repair sat in the open where the commits get read, and no released version contained it.

He knew. Look at his own words again: the chain is under risk at latest commit right now. At latest commit. He had been reading them.

None of this is hidden from you. The chain is a public database and it answers questions over the web. This is the transaction where the money came back:

1
curl -s https://blockstream.info/api/tx/a6d697a25266ce3c78774fd1d75f896b7af522ada209b0f6228ea497bc49a46d

Look in vout and you will see the two payments. Point the same thing at his address and the mailbag starts coming out:

1
curl -s https://blockstream.info/api/address/bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte/txs

That hands you the newest 25, plus whatever is still sitting unconfirmed. To walk back through the rest, take the txid of the last one you got and ask for the page behind it. Repeat until it comes back empty:

1
curl -s https://blockstream.info/api/address/bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte/txs/chain/TXID

The messages sit as hex in the scriptpubkey_asm field of the outputs marked op_return. Two of the push opcodes carry them: OP_PUSHBYTES for the short ones and OP_PUSHDATA for the long ones. Read only the first kind and you miss the best notes. Decoding them takes one line of Python.

Two things to take away from this.

  • โ†’ A patch that is public with no release behind it is an announcement. The people reading the commit logs of security software are not only the maintainers.
  • โ†’ Putting bitcoin behind something does not give it bitcoin’s security. The main chain was untouched. What failed was one stored answer in a cache, and fifteen signatures could not tell the difference.

You can pull these transactions and read the messages yourself with a browser and a terminal, no account and no wallet needed.

My Ethical Hacking Complete Course Zero to Expert takes you there step by step: reconnaissance, scanning, exploitation and traffic analysis, hands-on, from your first day with no Linux or hacking background.

โ†’ Join my complete ethical hacking course

Hacking is not a hobby but a way of life.

Sources:

Elements commit c26d719c | Blockstream Status | The return transaction

 
NEWSLETTER

Stay updated

Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.

By Bulls Eye

Jolanda de koff โ€ข email โ€ข donate

My name is Jolanda de Koff and on the internet, I'm also known as Bulls Eye. Ethical Hacker, Penetration tester, Researcher, Programmer, Self Learner, and forever n00b. Not necessarily in that order. Like to make my own hacking tools and I sometimes share them with you. "You can create art & beauty with a computer and Hacking is not a hobby but a way of life ...

I โ™ฅ open-source and Linux