ثغرة في محفظة Coldcard المادية تؤدي إلى سرقة أكثر من 88 مليون دولار في بيتكوين

iconMetaEra
مشاركة
AI summary iconملخص
أدى عيب في محفظة Coldcard المادية المرتبط بـ RNG غير مُهيأ بشكل صحيح إلى سرقة أكثر من 594 BTC، بقيمة تزيد عن 88 مليون دولار. أثر هذا الخلل على طرازات Coldcard Mk2 وMk3 وMk4 وQ وMk5، مما عرض المفاتيح الخاصة لهجمات القوة الغاشمة. وقد تتبع Beosin الأموال المجمعة إلى عدة عناوين بيتكوين. ويرفع هذا الحادث مخاوف ضمن CFT، ويشدد على ضرورة الامتثال لـ MiCA مع تشديد الاتحاد الأوروبي على تنظيمات العملات المشفرة.

خسائر تتجاوز 88 مليون دولار أمريكي: تحليل ثغرة محفظة Coldcard الصلبة وتتبع الأموال المسروقة في 31 يوليو، تم سرقة ما مجموعه 594 بيتكوين من حوالي 500 محفظة Coldcard صلبة، بقيمة تقارب 38 مليون دولار أمريكي. لاحقًا، أكّدت شركة Coinkite المسؤولة عن إنتاج محفظة Coldcard وجود ثغرة أمنية في عملية توليد المفاتيح، تؤثر على عدة إصدارات من المنتج، بما في ذلك Coldcard Mk2 وMk3 وMk4 وQ وMk5.

حاليًا، تجاوزت الخسائر الناتجة عن هجوم هذه الثغرة 88 مليون دولار أمريكي، ولا يزال الهجوم مستمرًا، ويجب على مستخدمي محفظة Coldcard نقل أموالهم إلى عناوين أخرى في أسرع وقت ممكن. فيما يلي تحليل Beosin للثغرة وحالة تتبع الأموال المسروقة.

أولاً: تحليل الثغرة

من خلال تحليل سجلات تقديم كود برنامج Coldcard، يمكن ملاحظة أنه في الالتزام السابق 37e4af5451c260c1e7d429fe8972c4cb5e68ee59، قام فريق التطوير بتحديث عدة أكواد تتعلق بتكوين MK4، بما في ذلك:

لدينا إصدارنا الخاص من هذا الكود. #define MICROPY_HW_ENABLE_RNG (0)

على جانب MicroPython STM32، يتحكم هذا الماكرو في مسار التجميع لمحول مولد الأرقام العشوائية المادية الافتراضي (RNG) وتنفيذ الأرقام العشوائية العام. تعيينه إلى 0 يؤدي إلى عدم استخدام مسار RNG المادي الافتراضي كخلفية لـ rng_get() العام.

ملاحظته تشير إلى أن المطور سيقوم بتنفيذ RNG بنفسه، وعند التحقق من ملف rng.h المخصص، وُجد أنه يُعلن عن كائنين اثنين من MicroPython:

MP_DECLARE_CONST_FUN_OBJ_0(pyb_rng_get_obj);MP_DECLARE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj);

التنفيذ المقابل هو:

/// \function pyb_rng_get()///// إرجاع رقم عشوائي مولد من الأجهزة بطول 30 بت: أو الفشل!//STATIC mp_obj_t pyb_rng_get(void){    // الحصول على الرقم العشوائي الجديد وإرجاعه    return mp_obj_new_int(rng_get_or_fault() >> 2);}
/// \function rng_get_bytes()/// ملء مخزن ببتات عشوائية؛ يجب على المستخدم توفير مخزن بحجم محدد.STATIC mp_obj_t pyb_rng_get_bytes(mp_obj_t buffer_io) {
    mp_buffer_info_t bufinfo;    mp_get_buffer_raise(buffer_io, &bufinfo, MP_BUFFER_WRITE);
    mp_uint_t count = bufinfo.len;    if(count < 1) {        mp_raise_ValueError(NULL);    }
    // قراءة كلمات بطول 32 بت وفكها إلى المخزن المقدم    random_buffer(bufinfo.buf, count);
    return mp_const_none;}
MP_DEFINE_CONST_FUN_OBJ_0(pyb_rng_get_obj, pyb_rng_get);MP_DEFINE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj, pyb_rng_get_bytes);

rng_get_or_fault() تقرأ ملحق RNG الخاص بـ STM32:

static uint32_t rng_get_or_fault(void){    // تمكين وحدة RNG إذا لم تكن ممكّنة بالفعل    rng_init();
    // الانتظار حتى جاهزية رقم عشوائي جديد، يستغرق حوالي 10 ميكروثانية    uint32_t start = HAL_GetTick();
    while (!(RNG->SR & RNG_SR_DRDY)) {        if (HAL_GetTick() - start >= RNG_TIMEOUT_MS) {            // فشل في الأجهزة... لا تُعد أي قيمة!            mp_raise_OSError(MP_EFAULT);        }    }
    // الحصول على الرقم العشوائي الجديد وعيده    last_value = RNG->DR;
    return last_value;}

هذا يوضح أن الكود المخصص لـ Coldcard يرغب فعلاً في استخدام RNG الهاردوير، لكنه يضمن استخدام وظيفة الهاردوير هذه فقط عند استدعاء pyb_rng_get* أو random_buffer() الداخلي.

وأثناء إنشاء المحفظة، يتم استدعاء الكود التالي فعليًا، حيث تكون وظيفة إنشاء المحفظة في shared/seed.py:

async def make_new_wallet(nwords):    # اختر بذرة عشوائية جديدة.    await ux_dramatic_pause('Generating...', 3)    seed = generate_seed()    words = await approve_word_list(seed, nwords)    if words:        await commit_new_words(words)

يتم إدخال هذا الاستدعاء أولاً في وحدة Coldcard/shared/random.py. في الإصدارات السابقة، كانت وحدة random.py تعتمد صراحةً على ngu.random، وتحتفظ بـ:

# random.py - مجموعة فرعية من وحدة random، بدون توافق، وتستخدم مولد أرقام عشوائية بجودة تشفيرية  
# لاستخدام البايتات، استخدم ngu.random.byte(len)  
# bytes = ngu.random.bytes

أي أن عملية تهيئة المحفظة تستخدم random.bytes، وليست pyb.rng()، ولم يتم التغطية التلقائية لـ random.bytes بواسطة pyb_rng_get_obj المخصصة.

بما أن MICROPY_HW_ENABLE_RNG تم تعيينها إلى 0، لم يتم استخدام RNG مادي أثناء توليد المحفظة، بل تم استدعاء pyb_rng_yasmarang من micropython/ports/stm32/rng.c:

#if MICROPY_HW_ENABLE_RNG
uint32_t rng_get(void) {    // استخدام مولد الأرقام العشوائية المادي من STM32    ...}
#else
// بالنسبة للمعالجات الصغيرة التي لا تحتوي على مولد أرقام عشوائية، لا نزال بحاجة إلى توفير دالة rng_get()// مولد الأرقام العشوائية الزائف ليس مثاليًا حقًا، لكننا نستخدمه حاليًا.
// مولد الأرقام العشوائية Yasmarangstatic uint32_t pyb_rng_yasmarang(void) {    static bool seeded = false;    static uint32_t pad = 0, n = 0;    ...}
uint32_t rng_get(void) {    return pyb_rng_yasmarang();}
#endif

أما pyb_rng_yasmarang فهو مولّد أرقام عشوائية زائفة، وهو غير آمن تمامًا لتوليد بذور محافظ الأجهزة، حيث يمكن للمهاجمين الحصول على المفاتيح من خلال الهجوم بالقوة الغاشمة. حاليًا، استبعدت Coinkite صراحةً ملف stm32/rng.c في ملف makefile:

لا تُركّب PRNG الاحتياطي لـ MicroPython. الملف rng.c الخاص باللوحة يوفر rng_get()، وهذا الكائن الفارغ يُرضي قائمة الكائنات الأساسية.  
$(BUILD)/rng.o: CFLAGS += -Dpyb_rng_yasmarang=error-do-not-want-this  
$(BUILD)/rng.o:  
$(ECHO) "SKIP stm32/rng.c"  
$(Q)$(CC) $(CFLAGS) -x c -c /dev/null -o $@

ثانيًا: تتبع الأموال المسروقة

حاليًا، تم نقل وأخذ أموال محفظات الضحايا المتعددة وجمعها في عناوين متعددة، ولم تُنظّف بعد. من خلال تحليل معلومات التهديدات والسلوك على السلسلة، رصدت Beosin Trace العناوين التالية للجمع:

bc1qq85v2c926eg6pgxhwp6q7lf6cnsz80qs3fcu9r (562 BTC)

https://wdcdn.qpic.cn/MTMxMDI3MDE1MTgxMDU0NzA_369595_usmImxZpHsKmu9X__1785899827?w=1080&h=656

bc1qx76cae2706qd5q576feh7xq8rfcsjpf2htfhe3 (398.47 BTC)

https://wdcdn.qpic.cn/MTMxMDI3MDE1MTgxMDU0NzA_304781_-IeWjFtIQ4RhvCbz_1785899827?w=1080&h=674

بالإضافة إلى ذلك، هناك عناوين جمع أخرى، وتدفق أموالها مشابه للرسم البياني أعلاه، ولا توجد أي تحويلات إضافية حاليًا:

  • bc1q8jy96fe5lf8vfugydnte3cguk92gpev7kwtp3q (89.62 BTC)
  • bc1q0rvn88w08j75k4h48lf9fvhan7unjp7vjf5q6m (64.9 BTC)
  • bc1qtfrwa4j6rmj9rsgspv6a0yjumkg39js2numu75 (45.9 BTC)
  • bc1qmd5m5ktv7m5ffujxv4248fxv36myvdx79n8jp6 (30.18 BTC)

لا تزال الهجمات على محفظة Coldcard مستمرة، ويبقى فريق Beosin يراقب بشكل مستمر عناوين جمع إضافية وينحل حركة الأموال ذات الصلة.

ثالثًا، الخاتمة

نشأت هذه الحادثة الأمنية الكبيرة في محفظة Coldcard المادية من خطأ في تنفيذ فريق التطوير لعملية توليد الأرقام العشوائية، حيث استُخدمت أرقام عشوائية زائفة في عملية توليد بذور المحفظة الأساسية. يجب على فريق التطوير إجراء اختبارات ومراجعة مستمرة وشاملة للرمز، ويجب على مستخدمي محفظة Coldcard نقل أصولهم في أسرع وقت ممكن، ومراقبة إعلانات الأمان القادمة من Coinkite عن كثب.

Beosin هي شركة رائدة في مجال أمن البلوكشين والامتثال التنظيمي، وتتخصّص في مراجعة أمان العقود الذكية قبل إطلاق المشروع، ومراقبة ومنع المخاطر الأمنية أثناء تشغيل المشروع، واسترداد الأصول المسروقة، ومكافحة غسل الأموال (AML) للأصول الافتراضية، والتحقيق والتتبع. وقد قدمت Beosin منتجات وخدمات متكاملة للامتثال لسلطة البلوكشين لـ 200 مزود خدمة للأصول الافتراضية و4500 مشروع Web3، بالإضافة إلى هيئات تنظيمية وإنفاذية في أكثر من 20 دولة ومنطقة حول العالم.

إخلاء المسؤولية: قد تكون المعلومات الواردة في هذه الصفحة قد حصلت عليها من أطراف ثالثة ولا تعكس بالضرورة وجهات نظر أو آراء KuCoin. يُقدّم هذا المحتوى لأغراض إعلامية عامة فقط ، دون أي تمثيل أو ضمان من أي نوع ، ولا يجوز تفسيره على أنه مشورة مالية أو استثمارية. لن تكون KuCoin مسؤولة عن أي أخطاء أو سهو ، أو عن أي نتائج ناتجة عن استخدام هذه المعلومات. يمكن أن تكون الاستثمارات في الأصول الرقمية محفوفة بالمخاطر. يرجى تقييم مخاطر المنتج بعناية وتحملك للمخاطر بناء على ظروفك المالية الخاصة. لمزيد من المعلومات، يرجى الرجوع إلى شروط الاستخدام واخلاء المسؤولية.