pull down to refresh

This is a really great analysis of how the coldcard attacker went about identifying and sweeping funds. The author is the maintained of Cove Wallet, @praveenperera.

The most interesting finding is that there are 153 addresses that were swept in the first wave, but which no researcher has been able to reconstruct the seeds for.

All 153 transactions use the same builder as the recovered set. The difference therefore appears upstream of transaction construction: candidate generation, device state, derivation coverage, or the data used to select targets.

The search history makes the gap more interesting than a normal incomplete scan.
  • Expanding BIP49 receive coverage past index 25 recovered a new Wave 1 seed at index 40.
  • Searching nonzero BIP84 accounts recovered two more Wave 1 seeds.
  • Checking more address indexes on both the receive and change branches recovered additional Wave 1 sources.
  • Nine alternative Coldcard event traces matched new candidate seeds to historically funded addresses outside Wave 1 and reproduced already-known Wave 1 sources, but found no new source in the 153.
  • Searches above the observed pad range, plus partial and evenly spaced tests across the full 32-bit pad range, found no new member of the 153.
  • An independently reported full raw-pad search also found none, although its full artifacts are not public.
Across the PRNG paths and address derivations I tested, I checked 104 billion candidate seeds and performed 5.60 trillion address checks, at a GPU cost of about $200. That search effort is why I think a missed path is unlikely, although a shared blind spot remains possible.

Praveen also also notes that no other researcher he has spoken with has reproduced a seed from one of these 153 addresses. Clearly, there is some missing piece of information or non obvious method that the attacker had but that the researchers lack.

Perhaps the information that allows researchers to figure out how the attacker swept these 153 addresses will reveal something about the approach that helps to identify the attacker.

The whole article is full of interesting observations and well worth the read. Here are a few highlights:

Once I had recreated the affected seed-generation process, I generated candidate seeds, derived their Bitcoin addresses, and compared those addresses with the Wave 1 transaction set. This linked 1,042 of the 1,195 sweep transactions to 328 reconstructed seeds. They account for 949.70395260 BTC, or 87.72% of the value. The final 153 transactions contain 132.94923662 BTC.

No researcher I have spoken with has reproduced a seed for any of those 153 source addresses. That repeated failure may be the most useful clue about what the attacker knew or did differently. The last part of this post examines that gap.
  • The final 153 source addresses, containing 132.94923662 BTC, remain unexplained. No researcher I have spoken with has reproduced a seed for any of them. They use the same transaction builder as the recovered set, so the missing difference appears to be earlier in the attacker’s process. Public reconstruction attempts may be missing an input or a candidate-generation method.
All 1,195 traceable source transactions have the same basic form:
  • transaction version 2;
  • locktime 0;
  • final input sequence 0xffffffff, so no opt-in RBF;
  • one destination output;
  • one source address per transaction;
  • a fee equal to 30 times the same static size estimate;
  • no low-R signature grinding.
The input order is more distinctive. There are 145 transactions with more than one input. All 145 spend the source address’s UTXOs from newest funding transaction to oldest. This is not BIP69 ordering, wallet coin selection, or the natural order produced by most general wallet software. It looks like an address API returned transaction references in reverse blockchain order and the sweep builder kept that order.
The results were still handled as separate address records. The 1,042 source transactions map to 328 seeds, with as many as 58 swept addresses from one seed. That behavior fits a process that derived standard paths, ranked addresses by balance, and saved address-and-key records without preserving the wallet as the unit of work.
I think the attacker used a paid blockchain-data API to discover balances and UTXOs, then signed the transactions with separate code. BlockCypher is the closest technical match I have found. Its Bitcoin address endpoint returns newest records first, defaults to 200 records, and tells the caller when another page exists. For the address with 225 UTXOs, I reproduced the exact 200 selected UTXOs, in the same order, with the live BlockCypher endpoint. This makes BlockCypher my leading hypothesis for the UTXO-discovery step.

~lol

I also suspect that whoever wrote the sweep code was more familiar with an account-based chain, such as Ethereum, than with Bitcoin wallet software. On Ethereum, ranking accounts by balance and sending one transfer per account is natural. The cost of a basic transfer does not depend on how many earlier deposits funded the account. Bitcoin value lives in separate UTXOs, so input selection affects the fee.

The transaction pattern is consistent with an account-balance mental model and weak Bitcoin wallet handling. The sweep kept addresses from the same seed as separate jobs, spent a 294-satoshi input that added about 2,040 satoshis to the fee, and left a 0.16083170 BTC UTXO behind an unprocessed page.

A weak-seed scanner naturally produces address records, and an address-based API encourages the same design.
reply
57 sats \ 0 replies \ @Akg10s3 22h

This answer is great!

reply

Maybe the 153 addresses was attacker ‘stealing’ from themselves? A sort of red herring?

reply

It seems like that would be a lot of added risk for the attacker with very little gain.

reply
2 sats \ 0 replies \ @fifoofa 14 Aug -30 sats

the 153 addresses nobody can rebuild a seed for is the scariest part of this. the rest all trace to the same RNG flaw, so the attacker had another derivation path or another leak nobody's found yet. and it cost two hundred bucks of gpu time to map a thousand btc drain, that ratio is the whole story