Back to Blog

COLDCARD Olayı: Bir Cüzdanın "Rastgele" Tohum Değeri Rastgele Olmadığında

Code Auditing
August 7, 2026
15 min read
Key Insights
  • COLDCARD'ın kayıpları, tek bir derleme yapılandırma hatasından kaynaklanmaktadır — bir makro koruyucu, değeri (#if) yerine varlığı (#ifndef) kontrol ederek, tohum üretimini sessiz sedasız deterministik bir yazılım RNG yedeğine yönlendirdi.

  • Saldırı tamamen çevrimdışıydı: zincir üzerinde herhangi bir istismar işlemi yoktu, yalnızca herkese açık cüzdan verileriyle eşleştirilen tohum numaralandırması (~Mk2/Mk3'te 40 bit, ~Mk4/Q/Mk5'te 72 bit) yapıldı.

  • Yazılımı güncellemek, önceden üretilmiş tohumları düzeltmez — güvenliği ihlal edilmiş fonlar, yamalı bir sürümde oluşturulan yeni bir tohuma taşınmalıdır.

  • Bu durum, kriptografik entropi yollarının neden yalnızca derleme zamanında değil, gönderilen yazılımda uçtan uca doğrulanması ve başarısızlık durumunda kapalı konuma geçecek şekilde tasarlanması gerektiğini ortaya koymaktadır.

Kısa Özet

30 Temmuz 2026'dan itibaren, COLDCARD Bitcoin donanım cüzdanlarından birden fazla dalga hâlinde fon çalınmaya başlandı ve bu süreç sonraki günlerde de devam etti. Tek bir zincir üstü saldırı işlemi veya açık bir sözleşme söz konusu değildi; kayıp, çevrimdışı bir seed kurtarma sorununun ardından gerçekleştirilen zincir üstü taramalardan kaynaklandı. 7 Ağustos 2026 itibarıyla zincir üstü takip, yaklaşık 4.925 adresten boşaltılan en az 1.405 BTC (~$91M, 7 Ağustos fiyatı olan $64.700 üzerinden) doğruladı [1], dalga düzeyinde atıf on dalga genelinde yaklaşık 1.433 BTC'ye ulaştı [2] ve mağdurlarla gerçekleştirilen özel kanal uzlaşması rakamı 2.055 BTC'ye (~$133M) kadar çıkardı [3].

Temel neden bir cüzdan entropi hatasıydı: 2021 yılındaki bir firmware geçişi, seed üretimini ngu.random.bytes() üzerinden yönlendirdi; bu işlev ise hedeflenen STM32 donanım RNG'si yerine deterministik bir yazılım üreteciyle devam etti. Etkilenen cihazlarda bu durum, seed kurtarmayı kriptografik olarak çözülemez bir problemden çevrimdışı bir aramaya indirgedi; böylece saldırgan, aday seed'leri sıralayarak bunları açık cüzdan verileriyle eşleştirebilir ve özel anahtarları kurtarabilir hâle geldi. Olayın daha önceki haftalık analizimizin üzerine inşa edilen bu derinlemesine inceleme, etkilenen her cihaz nesli için artık entropiyi nicelleştirir, mağdurların ve çalınan fonların zincir üstünde nasıl tespit edildiğini izler ve hotfix sonrası firmware'de oturum açmadan önce hizmet reddine yol açabilen ayrı bir gerilemeyi inceler.

Arka Plan

COLDCARD bir Bitcoin donanım cüzdanıdır. Diğer öz-gözetim cüzdanları gibi güvenliği, nihayetinde cüzdan kurulumu sırasında oluşturulan seed ifadesinin tahmin edilemezliğine bağlıdır. BIP-39 seed ifadesi insan tarafından okunabilir olmakla birlikte, temel güvenlik özelliği hâlâ onu oluşturmak için kullanılan rastgele baytların entropisidir. Seed üretim çıktısı tahmin edilebilirse, seed daha sonra ne kadar dikkatli saklanırsa saklansın cüzdanın güvenliği çöker.

COLDCARD, Mk1'den Mk5'e kadar olan donanım revizyonlarını ve Q modelini kapsar. Mk2 ve Mk3, eski firmware serisini kullanır. Mk4, Mk5 ve Q ise ayrı Standart ve Edge sürüm izlerini kullanır.

COLDCARD uygulama mantığının büyük bölümü, MicroPython üzerinde çalışan Python kodundan oluşur; donanım ve kriptografik işlemleri ise yerel C modülleri sağlar. Güvenli bir seed üretim yolu, STM32 donanım RNG'sinden 32 bayt okumalı, çevre birim durursa veya bir değeri tekrarlarsa başarısız olmalı, sonucu hash'lemeli ve ardından BIP-39 kelimeleri olarak kodlamalıdır. v3.2.2'de make_new_wallet() bu yolu izliyordu:

make_new_wallet()
  |- ckcc.rng_bytes(seed)
  |    `- random_buffer()
  |         |- STM32 RNG->DR'yi oku
  |         `- zaman aşımında veya tekrarlanan çıktıda başarısız ol
  |- SHA-256(seed)
  `- BIP-39 seed kelimeleri

Açık Analizi

Temel neden, MICROPY_HW_ENABLE_RNG etrafındaki bir derleme ve entegrasyon hatasıydı. COLDCARD'ın üretim kartı yapılandırması bu makroyu 0 olarak ayarladı; zira firmware, MicroPython'un donanım RNG uygulaması yerine kendi karta özel donanım RNG sarmalayıcısını kullanıyordu. Ancak cüzdan üretim yolu ngu.random.bytes(32) kullanımına geçirilmişti ve libngu'nun STM32 yolu nihayetinde global rng_get() sembolüne bağımlıydı.

Etkilenen yol, libngu'nun my_random_bytes() işlevine giriyordu. Her çıktı sözcüğü için CHIP_TRNG_32() tarafından döndürülen değeri kendi Yasmarang çıktısıyla XOR'luyordu:

generate_seed()
  `- ngu.random.bytes(32)
       `- my_random_bytes()                [libngu]
            |- CHIP_TRNG_32()
            |    `- rng_get()              [MicroPython]
            |         `- Yasmarang A
            |              `- UID + SysTick + RTC
            |
            |- my_yasmarang()              [libngu]
            |    `- Yasmarang B
            |         |- Mk2/Mk3: genel başlangıç durumu
            |         `- Mk4/Q/Mk5: 32 bitlik pad yeniden tohumlama
            |
            `- çıktı = Yasmarang A XOR Yasmarang B

Yasmarang A, rng_get() arkasındaki MicroPython geri dönüşüydü ve cihaz ile zamanlayıcı durumundan başlatılıyordu. Yasmarang B ise libngu'ya aitti: Mk2/Mk3'te genel başlangıç değerlerini kullanırken Mk4/Q/Mk5, yalnızca 32 bitlik pad'i güvenli element kaynaklı verilerle değiştiriyordu. Karta özel STM32 RNG hâlâ mevcuttu, ancak ngu.random.bytes() onu çağırmıyordu.

Uyumsuzluk, derleme yapılandırmasında doğrudan görünürdür. Mk4 mpconfigboard.h dosyası MicroPython'un donanım RNG dalını devre dışı bıraktı:

// Bu kodun kendi versiyonumuz var.
#define MICROPY_HW_ENABLE_RNG (0)

Libngu'nun CHIP_TRNG_32() işlevi, makronun varlığını donanım RNG'sinin yeterli kanıtı olarak değerlendirmeye devam etti; çünkü yalnızca makronun var olup olmadığını kontrol ediyor, ardından rng_get() çağırıyordu:

extern uint32_t rng_get(void);
#define CHIP_TRNG_32() rng_get()

#ifndef MICROPY_HW_ENABLE_RNG
#error "bir HW TRNG edinin lütfen"
#endif

Bu koruma, tehlikeli durumu gözden kaçırdı: 0 olarak tanımlanmış bir makro yine de tanımlıdır. Derleme bu nedenle başarılı oldu ve rng_get(), COLDCARD'ın ayrı karta özel sarmalayıcısı (random32() / random_buffer(), Python'a ckcc.rng_bytes olarak açık) yerine MicroPython'un STM32 RNG modülüne çözümlendi. MicroPython'un rng_get() seçiminde, MICROPY_HW_ENABLE_RNG == 0 yazılım geri dönüş dalını seçti:

#if MICROPY_HW_ENABLE_RNG
    // STM32 donanım RNG
#else
    // Yasmarang yazılım geri dönüşü
#endif

Mk2/Mk3: Yaklaşık 40 Bit

Derlenen pyb_rng_yasmarang() geri dönüşü, Yasmarang'ı şu şekilde başlattı ve ilerletti:

STATIC uint32_t pyb_rng_yasmarang(void) {
    static bool seeded = false;
    static uint32_t pad = 0, n = 0, d = 0;
    static uint8_t dat = 0;

    if (!seeded) {
        seeded = true;
        rtc_init_finalise();
        pad = *(uint32_t *)MP_HAL_UNIQUE_ID_ADDRESS ^ SysTick->VAL;
        n = RTC->TR;
        d = RTC->SSR;
    }

    pad += dat + d * n;
    pad = (pad << 3) + (pad >> 29);
    n = pad | 2;
    d ^= (pad << 31) + (pad >> 1);
    dat ^= (char)pad ^ (d >> 8) ^ 1;

    return pad ^ (d << 5) ^ (pad >> 18) ^ (dat << 1);
}

Değişkenler 104 bit kaplıyor, ancak durum boyutu entropi değildir. dat sıfırdan başlar; diğer değerler ise bağımsız sırlar yerine sabit meta veriler veya birbiriyle ilişkili zamanlayıcı okumaları niteliğindedir. Yaklaşık 40 bitlik değer için kullanılan gevşek Mk2/Mk3 modeli altında:

Girdi Aday değer sayısı Sıralama maliyeti
Bilinen UID_low32 1 2^0
SysTick->VAL 80.000 2^16,29
RTC->TR günün saati 86.400 2^16,40
RTC->SSR alt saniye 256 2^8

Tüm zamanlayıcı alanlarını bağımsız kabul etmek, kasıtlı olarak geniş bir üst sınır verir:

80.000 * 86.400 * 256
= 1.769.472.000.000
= 2^40,69 aday başlangıç durumu

Dolayısıyla kapsamlı bir arama en fazla 2^40,69 deneme gerektirir; tekdüze konum varsayımı altında ortalama yaklaşık 2^39,69 deneme yeterlidir. Bu bir sıralama üst sınırıdır, 40 bit kriptografik entropi değildir. Soğuk önyükleme sırasında RTC kayıtları statikse yalnızca SysTick kalır ve üst sınır yaklaşık 2^16,29'a düşer. UID, zamanlayıcılar ve önceki RNG çağrılarının sayısı biliniyorsa tam olarak bir akış vardır: 2^0. Bilinmeyen çağrı geçmişi, yalnızca makul yürütme izlerinin sayısını ekler; taze bir entropi kaynağı sunmaz. Aynı şekilde, 256 bitlik bir seed için sekiz 32 bitlik sözcük üretmek arama alanını çarpmaz: her sözcük aynı başlangıç durumu tarafından belirlenir.

Libngu katmanında my_random_bytes(), yukarıdaki MicroPython geri dönüşünü libngu'nun ayrı Yasmarang üreticisiyle harmanlıyordu. Kaynak kod chip = rng_get() ifadesini doğrudan içermez: CHIP_TRNG_32(), rng_get() olarak genişler. Mk2/Mk3'te ikinci üreteç genel sabitlerden başlıyordu:

static uint32_t yasmarang_pad = 0x0a8ce26f, yasmarang_n = 69, yasmarang_d = 233;
static uint8_t yasmarang_dat = 0;

uint32_t chip = CHIP_TRNG_32();
// ... bitişik çıktı sağlık kontrolü ...
chip ^= my_yasmarang();

Her iki akış da MicroPython geri dönüş durumu ve çağrı geçmişi bilindiğinde yeniden üretilebilirdi. XOR çıktı değerlerini değiştirdi ancak entropi eklemedi. UID_low32 bilinmese bile UID_low32 ^ SysTick, her iki girdiyi tek bir 32 bitlik pad'e daraltır; nominal bit sayıları toplanamaz.

Mk4/Q/Mk5: Yaklaşık 72 Bit

Sonraki modeller aynı çift üreteç yapısını korudu; ancak rng_seeding(), önyükleme sırasında libngu'nun üreticisine güvenli element materyali ekledi:

a = callgate.read_rng(1)       # SE1
b = callgate.read_rng(2)       # SE2

n = ngu.hash.sha256d(a + b)
n, = ustruct.unpack('I', n[0:4])
ngu.random.reseed(n)

Hash'e 40 bayt girse de yalnızca ilk dört baytı reseed() fonksiyonuna ulaştı [4]. random_reseed() uygulaması ardından yalnızca libngu'nun 32 bitlik pad sözcüğünü değiştirdi:

STATIC mp_obj_t random_reseed(mp_obj_t arg)
{
    yasmarang_pad = mp_obj_get_int_truncated(arg);
    return mp_const_none;
}

Diğer libngu durum sözcükleri genel değerlerini korudu ve MicroPython geri dönüşü yeniden tohumlanmadı. Yaklaşık 72 bitlik değer, 32 bitlik libngu yeniden tohumlama ile sonraki modellerin zamanlayıcı durumu için gevşek bir üst sınırı birleştirir:

MicroPython geri dönüşü:
    120.000 SysTick değeri * 86.400 RTC zamanı * 256 alt saniye
    = 2^41,27 durum

Libngu güvenli yeniden tohumlama:
    2^32 değer

Birleşik üst sınır:
    2^41,27 * 2^32 = 2^73,27 aday

Ortalama sıralama:
    2^73,27 / 2 = 2^72,27 deneme

"Yaklaşık 72 bit" ifadesinin kaynağı budur. Bu, güvenli elementler tarafından sağlanan 72 bit değil, ortalama saldırı çalışma tahminidir. Zamanlayıcı alanları birbiriyle ilişkilidir ve yeniden oluşturulabilir; MicroPython geri dönüş durumu biliniyorsa yalnızca 2^32 yeniden tohumlama değerleri kalır ve ortalama 2^31 deneme yeterlidir. Son 32 rastgele baytın hash'lenmesi, olası seed sayısını artıramaz.

Etkilenen Sürümler

Cihaz ve iz Bu gerilemenin dışında Etkilenen seed üreten firmware Etkin bit güvenliği İlk sabit sürüm
Mk1 v3.0.6'ya kadar Yok - Yok
Mk2/Mk3 v3.2.2'ye kadar v4.0.0-v4.1.9 (resmi duyuru v4.0.1'den) Etkilendiğinde yaklaşık 40 bit v4.2.0
Mk4/Mk5 Standart Yok v5.6.0 öncesi Düzeltme öncesi yaklaşık 72 bit; sonrası en az 128 bit v5.6.0
Q Standart Yok v1.5.0Q öncesi Düzeltme öncesi yaklaşık 72 bit; sonrası en az 128 bit v1.5.0Q
Mk4/Mk5 Edge Yok v6.6.0X öncesi Düzeltme öncesi yaklaşık 72 bit; sonrası en az 128 bit v6.6.0X
Q Edge Yok v6.6.0QX öncesi Düzeltme öncesi yaklaşık 72 bit; sonrası en az 128 bit v6.6.0QX

İlgili sürüm, şu anda kurulu olan firmware değil, seed'i üreten firmware'dir. Sabit sürümlerde veya sonrasında üretilen yeni seed'ler, düzeltilmiş yolu kullanır; ancak güncelleme mevcut bir seed'i onarmaz. Mk2/Mk3 için resmi etkilenen aralık v4.0.1'den başlar [5]; kaynak düzeyinde analiz ise v4.0.0'ı da kapsar [4]. Bağımsız zar entropisi seed'in güvenliğini artırabilirken, güçlü bir BIP-39 parolası, seed'in kendisini onarmadan ayrı bir bariyer ekler [6].

Web3 İçin En İyi Güvenlik Denetçisi

Lansmandan önce tasarımı, kodu ve iş mantığını doğrulayın

Mağdur ve Fon Takibi

İzlenecek bir zincir üstü açık işlemi yoktu; kurtarma zorunlu olarak çevrimdışıydı. Etkilenen firmware tarafından üretilmiş bir seed'i hedef alan bir saldırgan, yukarıda açıklanan aday RNG durumlarını kısıtlayıp sıralayabilir, her adayın seed üretim akışını yeniden oluşturabilir, elde edilen cüzdan anahtarlarını türetebilir ve bunları açık cüzdan verileriyle eşleştirip eşleşen fonlu cüzdanları zincir üstünde tarayabilir. Bu, yalnızca seed'in tek başına türettiği cüzdanlara ulaşabiliyordu: güçlü ve benzersiz bir BIP-39 parolası, RNG açığının hiç dokunmadığı bağımsız, kullanıcı tarafından sağlanan entropiyi PBKDF2 aracılığıyla anahtar türetmeye karıştırır ve bu tür cüzdanları saf seed sıralamasının dışında bırakır. Taranan cüzdanlar, zorunlu olarak bu tür koruması olmayanlardı [6]. Hırsızlık, izlenebilir bir açık yerine yalnızca zincir üstü taramalar olarak ortaya çıktığından, mağdurların tespit edilmesi ve fonların takibi zincir üstü adli bilişim meselesi hâline geldi.

Çalınan fonları birçok bağımsız çaba takip etti: kamuya açık takip siteleri (Coldcard Sweep Watch [1], coldcard.rip [2] ve Coldcard Hack Tracker [7]) ve Galaxy Research tarafından gerçekleştirilen özel kanal uzlaşması [3]; raporlanan toplamlar aşağıda karşılaştırılmaktadır. Coldcard Sweep Watch metodolojisini yayımladığından, tanımlama sürecini açıklamak için bunu kullanıyoruz; bu süreç, zincir dışı raporlar ile zincir üstü analiz arasında bir geri bildirim döngüsüdür:

  1. Zincir dışı çıpa noktaları. Mağdurlar ve araştırmacılar, mevcut olduğunda cihaz ve seed üretim bağlamıyla birlikte açık adresleri veya işlem kimliklerini iletti. Her rapor bir ipucu olarak değerlendirildi ve zincir üstünde doğrulandı; seed ifadesi, özel anahtar veya xpub gerekmedi.
  2. Zincir üstü genişleme. Doğrulanmış çıpa noktalarından başlayarak tarayıcılar, aynı tarama özelliklerini arayan ilgili blokları taradı: üzeri bozuk para olmadan boşaltılan cüzdanlar, benzer girdi türleri, sıkı zamanlama, tekrarlanan ücret oranları, ortak hedefler veya sonraki ortak harcamalar.
  3. Zincir dışı çapraz kontroller. Yeni mağdur raporları, araştırmacı veri kümeleri ve servis atamaları, aday dalgaları doğrulamak veya reddetmek için kullanıldı. Doğrulanmış kümeler ve buluşsal adaylar ayrı tutuldu.

Bitcoin adresleri tanımlar; kişileri veya cüzdan modellerini değil. Bir cüzdan birçok adresi kontrol edebileceğinden, adres sayısı mağdur sayısı değildir. İlk geniş çapta bildirilen büyük dalga olan 960188, 594,48 BTC oluşturuyordu; sürdürülen tarama ve raporlama toplamı artırdı. 7 Ağustos 2026 itibarıyla Coldcard Sweep Watch, yaklaşık 4.925 adresten doğrulanmış en az 1.405,07 BTC (~$91M, 7 Ağustos fiyatı $64.700 üzerinden) bildirdi [1]; 3 Ağustos coldcard.rip anlık görüntüsü ise on dalga genelinde 5.477 adresten brüt 1.433,13 BTC, ücretler sonrası hedeflere 1.432,48 BTC'yi nitelendirdi [2]. Mağdurlarla yazışmaya dayanan Galaxy Research tarafından gerçekleştirilen ayrı bir özel kanal uzlaşması, rakamı daha da yükseğe, yaklaşık 1.596 BTC'den 2.055 BTC'ye (~$133M, aynı fiyat üzerinden) çıkardı [3]. Farklılıklar, kanıt eşiklerini, keşif zamanını ve her takipçinin güvendiği doğrulama kanalını yansıtmaktadır.

Fonlar daha sonra gözlemlenen üç katman boyunca takip edildi: taranmış kaynak adresleri, doğrudan tarama hedefleri (holding) ve ardından gerçekleşen konsolidasyon hedefleri (vault). Tablo, her katmandaki ayrı adres sayısını göstermektedir:

Kalıp Örnek rotalar Takip sonucu
Birçok tarama bir veya iki holding adresine, ardından bir vault'a 960183: 204 -> 2 -> 1; 960188: 500 -> 1 -> 1 Hedef yakınsama, kümeyi nispeten güçlü ve takip etmesi kolay kılar
Taramalar, ilerleyen bir konsolidasyon olmaksızın holding adreslerinde durur 960352: 352 -> 1 -> 0; 960668: 795 -> 1 -> 0 Holding adresi izlenebilir kalır, ancak atfı güçlendirecek sonraki ortak harcama yoktur
Tarama başına taze hedefler; bazen ardından ayrı vault'lar 960359: 13 -> 13 -> 0; 960395: 1.918 -> 294 -> 293 Paylaşılan toplayıcı dedektörü başarısız olur; gruplama zamanlama, ücret oranı, işlem şablonu ve zincir dışı doğrulamaya bağlıdır

İzlenen çıktılar hareket ettiğinde analiz, ücretler sonrası değeri koruyarak ve atfı taranan miktarla sınırlayarak bölünmeleri, birleşmeleri ve soyma zincirlerini takip eder. Bir borsaya veya başka bir karışık hizmetine giren fonlar güveni azaltır; hizmet, saldırgan kümesine eklenmez.

MetaSleuth Soruşturmasını Keşfedin

Soruşturmalar için akışları izleyin ve kanıt oluşturun

Şimdi ücretsiz deneyin

Düzeltme Gerektiren Bir Düzeltme: Olası Bir Kilitleme Gerilememesi mi?

Entropi hatasından bağımsız olarak, onu çözen hotfix ayrı bir firmware gerilimi ortaya çıkardı [8, 9]. Donanım RNG yolunu geri yüklerken düzeltme, ele alınmamış bir donanım seed hatası durumu bıraktı; bu durum, oturum açmadan önce hizmet reddine yol açabilir ve cihazların kalıcı olarak kilitlendiğine dair iddialar gündeme getirdi. Bu daha güçlü iddianın ne ölçüde geçerli olduğu, register düzeyindeki ayrıntılara bağlıdır.

STM32 donanım RNG'si bir kontrol kaydı (RNG_CR), bir durum kaydı (RNG_SR) ve 32 bitlik bir veri kaydı (RNG_DR) sunar. İlgili durum şudur:

Bit Rol
RNGEN RNG'yi ve analog gürültü kaynaklarını etkinleştirir
DRDY RNG_DR'de verinin hazır olduğunu gösterir; yazılım yine de sıfırı reddetmelidir
SECS / SEIS Mevcut seed sağlık testi hatası / kilitlenmiş seed hatası durumu
CECS / CEIS Mevcut RNG saat arızası / kilitlenmiş saat hatası durumu

Mevcut ve kilitlenmiş durum arasındaki ayrım önemlidir. SECS, anlık gürültü kaynağı koşulunu tanımlarken SEIS, yazılım temizleyene kadar bir seed hatasının meydana geldiğini kaydeder. Mk4/Q ailesi STM32L4S'te bir seed hatası yeni rastgele sayı üretimini durdurur; Mk3 STM32L4'te ise veriler mevcut kalabilir ancak güvenilmemelidir. Saat hataları ayrıdır ve bu seed hatası kilitlenmesini oluşturmaz.

Gerekli kurtarma sırası, STM32 nesline bağlıdır:

Cihaz ailesi Belgelenmiş seed hatası kurtarma
Mk3 STM32L4 (RM0351, RNG hata yönetimi [10]) SEIS'i temizle, ardından RNGEN'i temizle ve tekrar ayarla
Mk4/Q ailesi STM32L4S (RM0432, RNG hata yönetimi [11]) SEIS'i temizle, 12 RNG_DR sözcüğünü oku ve at, ardından SEIS'in temiz kaldığını doğrula

31 Temmuz entropi hotfix'i rng_get()'i doğru şekilde donanım TRNG'ye çözümledi; ancak Mk4/Q ailesi rng_get_or_fault() hiçbir seed hatası kurtarması uygulamıyor. rng_init() yalnızca RNGEN kapalıyken harekete geçerken, okuma döngüsü yalnızca DRDY'yi kontrol eder. Bir seed hatası RNGEN'i etkin bırakıp DRDY'yi bastırırsa, başlatma işlevsiz hâle gelir; her okuma SEIS'i temizlemeden 10 ms bekler ve OSError(EFAULT) fırlatır.

Bu durum, oturum açmadan önce kullanıcı arayüzüne ulaşabilir. Hem sayı tuş takımı mempad._start_scan() hem de Q keyboard._start_scan(), tuş basımı kesmesinden tarama sıralarını karıştırır. Orada bir RNG istisnası, PIN girişini ve normal firmware yükseltme menüsünü o donanım oturumunun geri kalanı boyunca engelleyebilir.

Kod düzeyindeki başarısızlık yolu makuldür; ancak daha güçlü "kalıcı kilitleme saldırısı" iddiası kanıtlanmamıştır. RNG kontrol ve durum bitleri donanım sıfırlamada sıfıra döner; bu nedenle tek bir geçici hata çevre birimi kalıcı olarak hasar vermemelidir ve tam bir güç döngüsü boyunca kalıcılık gösterilmemiştir. Ayrıca seed sağlık testi hatasını tetikleyecek uzaktan veya güvenilir biçimde kontrol edilebilir bir yöntem gösterilmemiştir. Kilitlemenin doğrulandığını iddia eden bir X gönderisi [8], kendisinin de gösterdiği topluluk tarafından gönderilen PR #692 [9]'ye işaret etmektedir; ancak o PR'ın kendi yazarı, hatayı bir register taklidiyle analiz ettiğini, gerçek Mk4/Q donanımında yeniden oluşturmadığını ve saha raporlarını bağımsız olarak doğrulamadığını belirtmektedir. Bu nedenle en iyi desteklenen sınıflandırma, onaylanmış kalıcı bir kilitleme saldırısı değil, potansiyel bir oturum açma öncesi hizmet reddi ve güvenilirlik gerilimi olarak kalmaktadır.

Bakımcıların kendi düzeltmesi olan PR #693 [12], seed hatası bayraklarını kontrol eder, sınırlı kurtarma ve yeniden denemeler ekler, şüpheli örnekleri reddeder ve yalnızca beklenen tuş takımı hatasını yakalar; 5 Ağustos 2026'da birleştirildi ve topluluk PR #692 [9]'nin yerini aldı; bu PR 4 Ağustos 2026'da birleştirilmeden kapatıldı.

Sonuç

Bu olay, etkilenen cihazlar ve iş akışları için seed kurtarmayı kriptografik olarak çözülemezden çevrimdışı bir arama sorununa dönüştüren bir cüzdan entropi hatasıydı. Temel mühendislik başarısızlığı, gönderilen firmware'in güvenlik açısından kritik seed üretim API'sinin gerçekte hedeflenen donanım RNG'sine ulaştığını kanıtlamamasıydı. Derleme koruyucuları hem makro varlığını hem de makro değerini kontrol etmeli, kriptografik entropi için geri dönüşler kapalı başarısız olmalı ve CI, son firmware görüntüsünde sembol kaynağını ve uçtan uca entropi akışını doğrulamalıdır. Etkilenen kullanıcılar için çözüm, bir firmware güncellemesi değildir: güncelleme, hatalı yol altında zaten üretilmiş bir seed'i onarmaz ve sonradan parola eklenmesi, o seed'in adreslerinde zaten tutulan fonları korumaz. Bu fonların, sabit bir sürümde yeni bir seed'den oluşturulmuş bir cüzdana taşınması gerekir; yalnızca güçlü ve benzersiz bir parola arkasında olan fonlar, yalnızca seed sıralamasının dışında kaldı [6].

Referanslar

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit