
في المقالة السابقة ، تعاملنا مع هجمات خلل Vcc باستخدام ChipWhisperer. كان هدفنا الإضافي هو دراسة تدريجية لعملية قراءة ميكروكنترولر البرامج الثابتة المحمية. باستخدام مثل هذه الهجمات ، يمكن للمهاجم الوصول إلى جميع كلمات مرور الجهاز وخوارزميات البرنامج. مثال حي على ذلك هو اختراق محفظة تشفير أجهزة Ledger Nano S مع لوحة MK STM32F042 باستخدام هجمات خلل Vcc.
المهتمة؟ دعنا ننظر تحت القط.
لقد تعلمنا حول إمكانية قراءة البرامج الثابتة المحمية من مقال يعرض نتائج هجوم خلل Vcc - تجاوز بايت حماية RDP من خلال أداة تحميل الإقلاع لعدة وحدات تحكم دقيقة (المشار إليها فيما يلي - MK). نوصي أيضًا بقراءة المقالة حول كسر ESP32.
كان الأساس النظري للدراسة هو المبدأ التوجيهي لقراءة البرامج الثابتة المحمية بنجاح من أجل LPC1114 من خلال أداة تحميل الإقلاع المقنعة باستخدام ChipWhisperer.
كما في المقالة الأولى ، قررنا إجراء تجارب على لوحة MK STM32F103RBT6:

مجلس STM32F103RBT6
يتم تحديد القدرة على كتابة البيانات إلى قطاعات ذاكرة الفلاش وذاكرة الوصول العشوائي أو قراءتها ، وكذلك تنفيذ إجراءات أخرى مع ذاكرة MK حسب قيمة بايت الحماية (من أجل STM32 - RDP). بالنسبة إلى MK المختلفة ، تختلف القيم والغرض من بايتات الحماية ، بالإضافة إلى خوارزمية التحقق منها.
إعداد الأجهزة
لنبدأ التجربة. تحتاج أولاً إلى توصيل ChipWhisperer بـ MK وفقًا للشكل:

مخطط اتصال ChipWhisperer بـ STM32 لقراءة البرامج الثابتة المحمية من خلال أداة تحميل القناع
يتم شطب العناصر التي يجب إزالتها من لوحة STM32F103RBT6 في الرسم التخطيطي (على عكس اتصال MK القياسي). تشير الأسهم إلى نقاط اتصال ChipWhisperer ، وتشير التواقيع إلى دبابيسها.
لا يعد وجود الكوارتز الخارجي ، كما هو موضح في الرسم التخطيطي ، ضروريًا ، لأنه عند العمل مع أداة تحميل القناع ، يستخدم MK STM32F103RBT6 CLOCK داخلي بتردد 24 ميجا هرتز ، لذلك لا يوجد التزامن بين ChipWhisperer و MK.
دعنا ننتقل إلى إعداد ChipWhisperer. كما ذكر أعلاه ، فإن التردد الموصى به لـ ChipWhisperer هو 24 MHz (أو مضاعف آخر). كلما زاد تعدد هذا التردد ، زادت دقة ضبط وقت الهجوم. نظرًا لعدم وجود التزامن ، يكون اختيار المعلمة range.glitch.offset اختياريًا ؛ يمكن تعيين أي قيمة لها.
يجب تحديد المعلمات range.glitch.repeat و scope.glitch.width وفقًا للتردد المحدد لـ ChipWhisperer. مع قيمة تردد كبيرة ، يتم دمج جميع النبضات قصيرة الأجل ، التي يتم تعيين عددها باستخدام scope.glitch.repeat ، في نبضة واحدة طويلة. لذلك ، يمكنك تحديد قيمة المعلمة range.glitch.width ، و range.glitch.repeat fix ، أو العكس. لقد وجدنا أن مدة النبضة المثلى يجب أن تكون حوالي 80 نانوثانية (يتم تعريفها على أنها عرض النبضة بحد أقصى نصف).
يبقى لتحديد قيمة المعلمة range.glitch.ext_offset.
نطاق التحديد. glitch.ext_offset
تحتاج أولاً إلى اختيار لحظة الهجوم. وفقًا للمخطط المقدم في وثيقة شركة STM ، يتم التحقق من قيمة بايت الحماية بعد تلقي طلب لقراءة البيانات من قطاع الفلاش:

الخوارزمية للاستجابة لطلب قراءة البيانات من قطاع الفلاش
للتحقق من صحة نظام التحقق هذا ، قرأنا الكود القابل للتنفيذ من محمل الإقلاع لـ MK مشابه دون حماية RDP عبر ST-Link. توضح الأشكال أدناه أجزاء من خوارزمية معالجة أمر قراءة الذاكرة .

منظر عام لمعالجة أمر قراءة الذاكرة (الدعوة إلى وظيفة التحقق من RDP وإرسال NACK في حالة فشل التحقق مرئية بوضوح)

RDP هيئة التحقق من صحة الجسم
دعنا ننتبه إلى 0x40022000 + 0x1C
وظيفة التحقق من RDP: يمكن ملاحظة أن السجل قيد القراءة على 0x40022000 + 0x1C
، وهو تحول منطقي قدره 30 بت وتفرع. من وثائق دليل البرمجة PM0075 (STM32F10xxx ميكروكنترولر ذاكرة فلاش) ، يصبح من الواضح أن 0x40022000
هو العنوان الأساسي 0x40022000
تحكم ذاكرة الفلاش ، و 0x1C
هو إزاحة تسجيل FLASH_OBR ، الذي نحن مهتمون بت بت RDPRT الثاني: حماية القراءة ، والذي يحتوي على حالة حماية RDP.
تتمثل اللحظة الضرورية للهجوم في تطوير تعليمة LDR
(التحميل من الذاكرة). يوجد هذا الإرشادات بين طلب قراءة البرنامج الثابت (إرسال بايت 0x11
مع 0xEE
) واستجابة NOACK
/ NOACK
بواسطة UART. من أجل إصلاح هذه اللحظة بصريًا ، من الضروري توصيل الذبذبات بـ UART1_RX (pin PA10) و UART1_TX (pin PA9) ، ثم مراقبة تغيير الجهد وفقًا لـ UART1. نتيجةً لذلك ، يجب أن يبدو شكل موجة الهجوم القوي مع النطاق المحدد. glitch.ext_offset كالتالي:

اختيار لحظة الهجوم
قراءة البرامج الثابتة النصي
تحتاج الآن إلى تحديد لحظة بدء تشغيل مشغل CW_TRIG في رمز Python من أجل اعتراض لحظة إرسال المجموع الاختباري عبر UART1_RX. يحتوي ChipWhisperer على مكتبة للتواصل مع محمل قناع STM32 MK. في الوضع العادي ، يتم استخدام هذه المكتبة لتنزيل البرامج الثابتة من الكتيبات إلى MK باستخدام class STM32FSerial(object)
الموجودة في الملف programmer_stm32fserial.py
على طول software/chipwhisperer/hardware/naeusb/
المسار software/chipwhisperer/hardware/naeusb/
. لتنشيط المشغل ، تحتاج إلى نسخ هذه الفئة إلى البرنامج النصي القابل للتنفيذ الرئيسي بحيث تصبح طريقة الفصل CmdGeneric(self, cmd)
متاحة عالميًا ، وإضافة الأمر scope.arm()
قبل إرسال المجموع الاختباري (0xEE) لطلب قراءة قطاع الذاكرة. يتم إعطاء الفئة النهائية في المفسد أدناه.
فئة للتواصل ChipWhisperer مع STM32 import time import sys import logging from chipwhisperer.common.utils import util from chipwhisperer.hardware.naeusb.programmer_stm32fserial import supported_stm32f from chipwhisperer.capture.api.programmers import Programmer
تجدر الإشارة إلى أن أداة إزالة القناع STM32F1xx تسمح لك بقراءة ما لا يزيد عن 256 بايت من البرامج الثابتة من قطاع فلاش محدد في طلب واحد. لذلك ، عند قراءة البرنامج الثابت بالكامل لـ MK ، من الضروري إجراء العديد من طلبات القراءة أثناء الهجوم خلل Vcc. ثم ، يجب تقسيم البايتات المستلمة 256 إلى ثمانية صفيفات 32 بايت وتشكيل ملف HEX منها.
HEX رمز المحول والوظائف الإضافية def int2str_0xFF(int_number, number_of_bytes): return '{0:0{1}X}'.format(int_number,number_of_bytes_in_string) def data_dividing_from_256_to_32_bytes (data_to_divide, mem_sector, mem_step=32): if mem_sector > 0xFFFF: mem_conversion = mem_sector >> 16 mem_conversion = mem_sector - (mem_conversion << 16) data_out = '' for i in range(int(256/mem_step)): data_vector = data_to_divide[(i * mem_step):((i + 1) * mem_step)] mem_calc = mem_conversion + (i * mem_step) data_out += read_and_convert_data_hex_file(data_vector, mem_calc, mem_step) + '\n' return data_out def read_and_convert_data_hex_file(data_to_convert, memory_address, mem_step): addr_string = memory_address -((memory_address >> 20) << 20) data_buffer = '' crcacc = 0 for x in range(0, len(data_to_convert)): data_buffer += int2str_0xFF(data_to_convert[x], 2) crcacc += data_to_convert[x] crcacc += mem_step temp_addr_string = addr_string for i in range (4, -1, -2): crcacc += temp_addr_string >> i*4 temp_addr_string -= ((temp_addr_string >> i*4) << i*4) crcacc_2nd_symbol = (crcacc >> 8) + 1 crcacc = (crcacc_2nd_symbol << 8) - crcacc if crcacc == 0x100: crcacc = 0 RECTYP = 0x00 out_string = ':'+ Int_To_Hex_String(mem_step, 2) +\ Int_To_Hex_String((addr_string),4) +\ Int_To_Hex_String(RECTYP, 2) +\ data_buffer +\ Int_To_Hex_String(crcacc, 2) return out_string def send_to_file(info_to_output, File_name, directory): file = open(directory + File_name + '.hex', 'w') file.write(info_to_output) file.close() def reset_target(scope): scope.io.nrst = 'low' time.sleep(0.05) scope.io.nrst = 'high' from collections import namedtuple Range = namedtuple('Range', ['min', 'max', 'step'])
تم الآن تكوين إعدادات ChipWhisperer. البرنامج النصي النهائي لقراءة البرنامج الثابت كما يلي:
علقت جميع رسائل print()
بعد السطر except Exception as
مراقبة حالة MC عند البحث عن المعلمات المثلى لنبض العطب. لتتبع حالة MK المحددة ، يكفي إلغاء رسالة print()
اللازمة.
قراءة النتائج
يعرض الفيديو تنزيل البرنامج الثابت إلى MK من خلال مبرمج ST-LINK ، ونقل RDP إلى حالة الحماية ثم قراءة البرنامج الثابت:
الأخطاء التالية قد تمنع الهجمات الناجحة Vcc:
• قراءة القطاع الخطأ من الذاكرة ؛
• الإزالة التلقائية للبرامج الثابتة.
الاختيار الدقيق للحظة الهجوم عن طريق زيادة وتيرة ChipWhisperer سيساعد على تجنب مثل هذه الأخطاء.
بعد تطوير وتصحيح الخوارزمية لقراءة البرامج الثابتة المحمية ، أجرينا قراءة اختبار للبرامج الثابتة الخاصة بمبرمج ST-LINK-V2.1 ، والذي يعمل على STM32F103CBT6 MK. بعض البرامج الثابتة ، قمنا بخياطة MK STM32F103CBT6 "نظيف" وتثبيته بدلاً من المصنع. ونتيجة لذلك ، عملت ST-LINK-V2.1 مع MK استبداله في الوضع العادي ، كما لو لم يكن هناك بديل.
حاولنا أيضًا إجراء سلسلة من الهجمات على STM32F303RCT7. تصرف MK أثناء الهجوم بشكل مماثل لـ STM32F103RBT6 ، لكن الاستجابة لطلب الذاكرة المقروءة احتوت على بايت يساوي 0x00 ، وهو ما لم يتزامن مع النتيجة التي توقعناها. كان سبب هذا الفشل مبدأ أكثر تعقيدًا وتطويرًا لتنظيم حماية أعضاء الكنيست.
يوجد حالتان للحماية في جهاز STM32F1xx MK: الحماية متوقفة (المستوى 0) وعلى (المستوى 1). في الموديلات الأقدم ، توجد ثلاث حالات حماية: تم تعطيل الحماية (المستوى 0 ، RDP = 0x55AA) ، وحماية ذاكرة الفلاش وذاكرة SRAM (المستوى 2 ، RDP = 0x33CC) وحماية ذاكرة الفلاش فقط (المستوى 1 ، تأخذ RDP أي قيم بخلاف من 0x55AA و 0x33CC). نظرًا لأن المستوى 1 يمكن أن يأخذ العديد من قيم RDP ، فإن إعداد المستوى 0 صعب للغاية. من ناحية أخرى ، من الممكن خفض مستوى الحماية من المستوى 2 إلى المستوى 1 عن طريق هدم بت واحد في بايت RDP (كما هو موضح في الشكل أدناه) ، والذي يسمح بالوصول إلى ذاكرة SRAM.

مقارنة قيم RDP لمستويات مختلفة من حماية البرامج الثابتة
يبقى فقط أن نفهم كيف يمكن للمهاجم الاستفادة من هذا. على سبيل المثال ، باستخدام طريقة CBS (Cold-Boot Stepping) الموضحة في هذه المقالة . تعتمد هذه الطريقة على لقطة تدريجية لحالة ذاكرة SRAM (تردد كل لقطة في الميكروثانية) بعد تحميل MC من أجل الحصول على مفاتيح التشفير أو كلمات المرور المخفية أو أي معلومات قيمة أخرى. يقترح المؤلفون أن طريقة CBS ستعمل على جميع سلاسل MK STM32.
النتائج
لتلخيص تجاربنا. استغرق الأمر منا عدة أيام لإكمال هجوم خلل Vcc باستخدام البيانات التي تم الحصول عليها من دراسة سابقة (والتي يمكن قراءتها هنا ). لذلك ، تعلم كيفية تنفيذ مثل هذه الهجمات هو سهل بما فيه الكفاية.
تعتبر هجمات Vcc-خلل خطيرة لأن من الصعب الدفاع عنها. لتقليل احتمال إجراء مثل هذه الهجمات بنجاح ، يُقترح استخدام MK بمستوى أعلى من الحماية.

راكون سيكيوريتي هو فريق خاص من الخبراء في مركز فولكانو العلمي والتقني في مجال أمن المعلومات العملي ، والتشفير ، والدوائر ، والهندسة العكسية ، وإنشاء برامج منخفضة المستوى.