Атакуващ изпразни 1196 биткойн портфейла за 41 минути и открадна 70 милиона долара. Собствениците не са направили нищо нередно. Техният хардуерен портфейл тихо е генерирал предвидими ключове от 2021 г. насам.

Хардуерният портфейл продава едно обещание: вие съхранявате ключа си на малко устройство, което никога не се свързва с интернет, и няма как атакуващият да достигне до него. Устройството може да стои изключено в сейф на друг континент. Обещанието беше, че ключът не може да бъде отгатнат. Хората го разбраха като обещание, че ключът не може да бъде достигнат. Тази кражба беше насочена срещу първото обещание и никога не е докосвала нито едно физическо устройство.

Въпросното устройство е Coldcard – хардуерен портфейл за биткойни, произвеждан от канадската компания Coinkite. За да разберете какво се е счупило, трябва да знаете едно нещо за това как работят тези портфейли.

Когато създавате портфейл, устройството избира едно огромно число. Това число е началното число (семето). Адресите, които получавате, и частните ключове, които харчат вашите монети, произлизат от това семе чрез фиксирани, публични правила. Единственото нещо, което защитава вашите монети, е това, че семето не може да бъде отгатнато, тъй като се извлича от специализиран хардуерен чип, чиято работа е да генерира истинска произволност.

При засегнатия фърмуер обаче то не е било извлечено от този чип.

През март 2021 г. кодът на портфейла е реорганизиран. Генерирането на семена е преместено от функцията, четяща хардуерния чип за произволност, към нов път в библиотека. Този път тихо е преминал към софтуерен заместител – малък предвидим генератор, вграден в MicroPython, вместо да използва хардуерния източник, за който е бил предназначен. Нищо не се е сринало и нищо не е предупредило потребителя. Портфейлът е продължил да създава семена и те са изглеждали произволни.

Причината това да се изплъзне при прегледа на кода се крие в един-единствен ред. Библиотеката е имала предпазен механизъм, предназначен да спре компилацията, ако хардуерната произволност не е включена. Този механизъм обаче използва #ifndef, което проверява само дали настройката съществува, а не дали е включена. Конфигурацията на платката е дефинирала настройката и я е задала на нула, тъй като портфейлът има своя отделна хардуерна рутина. Дефинирана, но нула. Предпазният механизъм е видял, че тя съществува, останал е тих и компилацията е преминала успешно. Библиотеката е изисквала произволност чрез определено име на функция. Собствената хардуерна рутина на портфейла е била с различно име, а софтуерният алтернативен вариант е бил единственият, предлагащ името, което библиотеката е искала, така че компилацията се е свързала с алтернативния вариант. И двата варианта са имали еднаква структура, така че нищо не е сигнализирало за грешка.

Софтуерният алтернативен вариант се инициализира веднъж, при първото повикване, от стойности, които не са тайна. UID е фиксиран фабричен сериен номер, вграден в чипа, като част от него дори се появява в USB серийния номер на портфейла. SysTick и RTC регистрите са стойности на часовника и таймера, които атакуващият може да стесни или измери на собствено устройство. След този единствен момент не постъпва нова произволна информация. Оттам нататък изходният резултат е напълно детерминиран.

Резултатът е, че броят на семената, които портфейлът изобщо би могъл да произведе, се е свил от невъобразимо голям до диапазон, който компютър може да обходи.

При по-старите портфейли Mk2 и Mk3, работещи със засегнатия фърмуер, кодът не е добавял никаква сигурна произволност. При известен сериен номер и състояние на таймера, семето е напълно детерминирано. Производителят оценява ефективната сила на около 40 бита, срещу 128 бита, които едно семе трябва да има.

При по-новите модели по-късна промяна е смесила малко произволност от защитения елемент, но е запазила само четири байта от нея и е нулирала само една 32-битова стойност. Това оставя най-много четири милиарда възможни резултата. Четири милиарда е голямо число за човек, но е нищожно за компютър.

Това е достатъчно. Атакуващият генерира потенциални семена на собствената си машина, извлича адресите, които всяко от тях би произвело, и ги проверява спрямо публичния блокчейн, който е свободен за изтегляне. Съвпадението означава финансиран портфейл. Публичният адрес е това, спрямо което се проверява всяко предположение. Устройството на жертвата никога не участва и може да е било изключено в чекмедже, докато това се случва.

Galaxy Research картографира целия обхват на атаката и откри отпечатък на машина, а не на целенасочен удар. Източените портфейли са използвали три различни формата на адреси, смесени заедно: 1183 в модерния формат, седем в по-стар и шест в още по-стар формат. Крадец, преследващ една конкретна жертва, не би докоснал три формата наведнъж. Това е бил скенер, обхождащ всеки път на деривация и взимащ всичко, което намери.

Източването е станало между 01:10 и 01:51 UTC на 30 юли. Общо 1082,65 биткойна са откраднати от 1196 портфейла в рамките на шест блока, с три празни блока между тях, където нищо не се е движило. Монетите са се приземили в четири адреса и оттогава не са местени. Първите доклади оценяваха загубата на близо 38 милиона, тъй като по това време беше видим само един от тези четири адреса. Когато и останалите се появиха, сумата нарасна до около 70 милиона.

Уязвимостта е влязла в разпространявания фърмуер през март 2021 г. и е престояла там...