
أداة تقييم أمان بلوتوث للسماعات اللاسلكية المتأثرة بسلسلة ثغرات Airoha SDK (CVE-2025-20700/20701/20702) بدون دونجل وبدون صلاحيات الجذر
الإصدار 1.0.0
أداة تقييم أمان بلوتوث لسماعات الأذن اللاسلكية المتأثرة بسلسلة ثغرات SDK الخاصة بـ Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702). تقوم بمسح الأجهزة القريبة، وتحديد بصمات الشرائح المعروفة المتأثرة القائمة على Airoha، وفحص الوصول غير الموثَّق لـ GATT وقابلية الوصول لبروتوكول RACE - بالكامل عبر مكدس بلوتوث نظام التشغيل (BlueZ) عبر bleak. لا حاجة لدونجل بلوتوث خارجي ولا لجذر. يتم عرض النتائج بلغة واضحة إلى جانب التفاصيل الفنية، حتى تتمكن من التصرف بناءً عليها دون معرفة عميقة بالبلوتوث.
هذه الأداة مخصصة لتقييم الأجهزة التي تمتلكها أو لديك إذن صريح باختبارها. فحوص GATT و RACE هي عمليات نشطة: فهي تتصل بالجهاز المستهدف وترسل إليه أوامر. لا تقم بتشغيل --gatt أو --race أو --firmware أو --bd-address أو --assess أو --baseline أو --check-drift أو --memory-read ضد جهاز ليس ملكك أو لم تحصل على إذن لاختباره. --scan هو فحص سلبي ويستمع فقط إلى الإعلانات التي تُبث علنًا بالفعل، لذا فهو آمن للتشغيل ضد أي جهاز في النطاق.
--memory-read يذهب خطوة أبعد من الفحوص النشطة الأخرى: فهو يسترد صفحة واحدة حقيقية وقابلة للقراءة فقط (256 بايت) من محتوى الفلاش الفعلي للجهاز، على عنوان ثابت، كتأكيد قاطع لـ CVE-2025-20702 عندما لا يتلقى فحص --race القائم على قابلية الوصول فقط أي استجابة. وهو قابل للقراءة فقط (قراءات الفلاش لا تحمل أي مخاطر تآكل أو تلف، على عكس أوامر الكتابة/المسح/FOTA، التي لا ترسلها هذه الأداة أبدًا)، واختياري، ويتطلب تأكيدًا منفصلاً خاصًا به يتجاوز المطالبة القياسية بملكية الجهاز التي تصف ما يفعله بالضبط قبل تشغيل أي شيء.
فحص جهاز عشوائي قريب ليس مجرد مسألة سياسة - بل يمكن أن يكون له آثار جانبية حقيقية. يحاول --gatt قراءة أو الاشتراك في الإشعارات لكل خاصية (characteristic) يجدها، وبعض الأجهزة الاستهلاكية تكشف خدمات من نوع الإعداد (مثل خدمة Fast Pair من Google) التي تتفاعل مع ذلك عن طريق بدء مصافحة إقران حقيقية على الجهاز المستهدف، بغض النظر عن أي شيء تطلبه هذه الأداة صراحةً. خاصية تتطلب تشفيرًا يمكن أن تؤدي إلى نفس الشيء حتى ضد جهازك الخاص، نظرًا لأن BlueZ يمكنه توجيه طلب المصادقة هذا بصمت إلى أي وكيل سجلته بيئة سطح المكتب لديك (مثل مطالبة الإقران في KDE) - لذلك يقوم كل أمر نشط أيضًا بتسجيل وكيل BlueZ مؤقت خاص به يرفض تلقائيًا أي طلب من هذا القبيل طوال مدة الفحص، بحيث لا يمكن أن تظهر أي مطالبة إقران على الإطلاق. كل أمر نشط لا يزال يطلب تأكيدًا على أن العنوان المستهدف هو ملكك قبل القيام بأي شيء عبر الراديو؛ مرّر --yes لتخطي المطالبة للاستخدام النصي بمجرد تأكيد أن الجهاز ملكك:
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --yes
--watch هو أمر سلبي، مثل --scan - فهو يستمع فقط إلى الإعلانات التي تُبث بالفعل ولا يتصل بأي شيء أبدًا، لذلك لا يطلب تأكيدًا.
python3 -m venv venv
venv/bin/pip install -r requirements.txt
يتطلب Python 3.10+ (طُوِّر ضد 3.14) ونظام Linux يعمل بـ BlueZ مع محول بلوتوث قيد التشغيل.
لينكس فقط، وليس بالضرورة كل نظام لينكس:
bleak نفسه لديه خلفية لنظام Windows، لكن هذه الأداة لا تعتمد على bleak فقط - اكتشاف البلوتوث الكلاسيكي (core/scanner.py) وفحوص حالة الإقران (core/gatt.py) كلاهما يستدعيان مباشرة bluetoothctl، وهي أداة CLI خاصة بـ BlueZ غير موجودة على Windows. تلك المسارات البرمجية ستفشل ببساطة مع "الأمر غير موجود."bluetoothctl في PATH، وليس مجرد أي نواة لينكس. معظم توزيعات سطح المكتب تأتي بهذا؛ الصورة الخفيفة أو الخادم بدون حزمة bluez المثبتة لن تحتوي عليه افتراضيًا. تم التحقق من أنه يعمل بدون جذر على BlueZ 5.86 - الإصدارات الأخرى يجب أن تعمل بنفس الطريقة لأن bleak يستهدف واجهة D-Bus القياسية لـ BlueZ، ولكن لم يتم إعادة التحقق بشكل مستقل.usbipd-win، الذي يوجه فقط المحولات المتصلة عبر USB. معظم أجهزة اللابتوب المزودة ببلوتوث مدمج موصولة عبر ناقل غير USB (SDIO/PCIe، بجانب Wi-Fi)، والذي لا يمكن لـ usbipd-win توجيهه بشكل عام - لذا فهذا يعتمد كليًا على الأجهزة المحددة.لست بحاجة إلى جهاز لينكس خاص بك - كل ما تحتاجه هو لينكس مع وصول حقيقي إلى راديو بلوتوث. طريقتان عمليتان للحصول على ذلك:
على أي حال، القاعدة هي نفسها: الأداة نفسها لم تتغير - إنها تحتاج فقط إلى لينكس مع محول بلوتوث يمكن لـ BlueZ الوصول إليه فعليًا.
العديد من سماعات TWS اللاسلكية تتوقف عن الإعلان (وتقطع أي اتصال نشط) بعد فترة من عدم النشاط لتوفير الطاقة، وبعضها ينطفئ تمامًا من تلقاء نفسه. إذا لم يتمكن الفحص من العثور على جهاز وجده قبل دقيقة، أو فشل الفحص في منتصف الطريق، فهذا عادةً ما يكون بسبب ذهاب السماعات إلى حالة الخمول، وليس خطأ - أخرجها من العلبة أو اضغط على زر الإقران مرة أخرى وأعد المحاولة.
يؤثر هذا أيضًا على استقرار العنوان: وحدة الاختبار المؤكدة لهذا المشروع (Sony WF-1000XM3) احتفظت بنفس عنوان BLE عبر كل دورة طاقة تم اختبارها، وهو أمر متوقع للسماعات المصممة لإعادة الاتصال بتطبيق الرفيق - فهي عادةً ما تستخدم عنوان BLE ثابت/عام بدلاً من عنوان متغير (على عكس الهواتف، التي تغير العناوين الخاصة وليست هدفًا مناسبًا لهذه الأداة لهذا السبب). لكن هذا ليس مضمونًا لكل موديل سماعة - بعض البائعين يستخدمون عناوين خاصة قابلة للحل حتى في وضع ما قبل الإقران/إعادة الاتصال، مما سيظهر كعنوان مختلف بعد كل دورة طاقة لماسح ضوئي غير مقترن مثل هذه الأداة.
فحص GATT (--gatt، ومرحلة GATT من --assess) قد يحتاج إلى عدة إعادة اتصالات إذا كان الجهاز يحتوي على خصائص تتطلب إقرانًا - كل واحدة منها تجعل BlueZ يحاول (ويقوم وكيل هذه الأداة برفض) مفاوضات إقران حقيقية قبل إعادة الاتصال لاستئناف الفحص، ويطبع سطر حالة واحد قبل كل محاولة حتى لا يبدو الفحص البطيء وكأنه معلق. عندما يتم رفض هذا الاشتراك، يحتفظ BlueZ بالقصد ويعيد إصداره في كل اتصال لاحق بهذا الجهاز؛ لمنع ذلك من عرقلة إعادة الاتصالات اللاحقة، يقوم الفحص بمسح السجل المخبأ لـ BlueZ للجهاز (مكافئ لـ bluetoothctl remove) قبل كل إعادة اتصال، بحيث تبدأ كل محاولة من حالة نظيفة. مع ذلك، تعيد عمليات الفحص المتكررة المتتالية ضد جهاز الاختبار المؤكد نفس النتيجة الكاملة في كل مرة. ملاحظة سابقة - أن الاكتمال يبدو أنه يتدهور على مدار جلسة اختبار مكثفة ويتعافى بعد الراحة - لم تتكرر منذ ذلك الحين، ويُعتقد أنها كانت نفس تراكم الحالة المحتجزة وليس إرهاق الجهاز.
buds_audit.py
تشغيلها بدون أي علامات يطلق قائمة مرقمة بدلاً من مطالبتك بمعرفة عنوان BLE بالفعل أو أي علامة تفعل ماذا:
1) تحليل كامل (مسح، تشغيل تدقيق CVE الكامل، وحفظ خط أساس)
2) التحقق من الحالة الحالية مقابل خط أساس محفوظ
3) مسح الأجهزة المزيفة/المنتحلة
4) خروج
الخيار 1 يقوم بمسح الأجهزة القريبة المعروفة المتأثرة ويسردها لتختارها برقم (بدلاً من كتابة عنوان MAC)، ثم يشغل تدقيق CVE الكامل (مثل --assess، بما في ذلك استعلام عنوان BD)، ويحفظ خط أساس (مثل --baseline) حتى تتمكن التشغيلات المستقبلية من اكتشاف التغييرات. كما يطرح نفس سؤال قراءة الذاكرة الذي يجيب عليه --assess --memory-read عبر مطالبة التأكيد الخاصة به - الإجابة بنعم هناك تتضمن نفس قراءة صفحة فلاش RACE الحقيقية القابلة للقراءة فقط الموضحة أعلاه؛ الإجابة بلا تشغل التدقيق فقط بدونها، ولا تلغي التحليل بأكمله. الخيار 2 يسرد الأجهزة التي لديك خط أساس لها بالفعل ويعيد فحص الجهاز الذي تختاره بحثًا عن الانحراف (مثل --check-drift). الخيار 3 هو --watch. كل خيار لا يزال يمر بنفس تأكيد الملكية مثل الواجهة القائمة على العلامات قبل لمس الراديو - المعالج هو واجهة أمامية أسهل على نفس الفحوص الأساسية تمامًا، وليس مسارًا منفصلاً أقل حذرًا.
الواجهة القائمة على العلامات أدناه لا تزال موجودة للاستخدام النصي أو لأي شخص يعرف بالفعل العنوان الذي يريد استهدافه.
يتم تشغيل جميع الأوامر عبر venv/bin/python buds_audit.py.
buds_audit.py --help
يعمل من python3 buds_audit.py --help العارية حتى بدون venv وبدون تثبيت التبعيات - فهو لا يستورد bleak حتى يتم تشغيل أمر يحتاج فعليًا إلى الراديو.
buds_audit.py --scan
buds_audit.py --scan --flags-only # يُظهر فقط الأجهزة المطابقة للكتالوج المعروف المتأثر
buds_audit.py --scan --target AA:BB:CC:DD:EE:FF
يقوم بمسح سلبي لأجهزة BLE والبلوتوث الكلاسيكي القريبة، ويحدد بصمات شرائح Airoha من بيانات الشركة المصنعة وبادئة العنوان، ويقارنها مع data/affected_devices.json.
كل منها يتطلب --target ADDR وهو عملية نشطة ضد ذلك الجهاز الواحد:
buds_audit.py --gatt --target AA:BB:CC:DD:EE:FF # CVE-2025-20700: وصول غير موثَّق لـ GATT
buds_audit.py --race --target AA:BB:CC:DD:EE:FF # CVE-2025-20702: قابلية الوصول لقناة RACE
buds_audit.py --firmware --target AA:BB:CC:DD:EE:FF # CVE-2025-20701: فحص سلبي للبرامج الثابتة/تجاوز الإقران
buds_audit.py --bd-address --target AA:BB:CC:DD:EE:FF # عنوان BD الكلاسيكي عبر RACE، معلوماتي
جميع الأربعة تتخطى بشكل نظيف (بدون خطأ) إذا كان الجهاز مقترنًا بالفعل - فنتيجة "وصول غير موثَّق" لا معنى لها ضد جهاز مقترن.
--gatt يعرض الآن القيمة الفعلية التي أرجعتها كل قراءة ناجحة غير مقترنة أو إشعار (مشفرة بالنظام الست عشري)، وليس فقط أن القراءة نجحت - القيمة كانت تُسترد بالفعل، لذا لا يوجد خطر إضافي، إنها فقط لم تعد تُهمل.
--race يختبر فقط قابلية الوصول (استعلام معلومات SDK غير ضار، بدون وصول إلى الذاكرة) - قد تكون خدمة RACE موجودة وتقبل الكتابة بشكل نظيف ولكنها لا ترد، وهي نتيجة غير حاسمة حقًا، وليست دليلاً على أن أي شيء قد أُصلح. للحصول على إجابة قاطعة، انظر --memory-read أدناه.
--bd-address معلوماتي، وليس نتيجة ثغرة بحد ذاته: فهو يستعلم عنوان البلوتوث الكلاسيكي (BR/EDR) الحقيقي للجهاز عبر نفس قناة RACE غير الموثَّقة، بنفس شكل المخاطرة مثل استعلام buildversion الخاص بـ --firmware (أمر بيانات وصفية بدون حمولة). مفيد إذا كنت ترغب في متابعة الاختبار النشط لـ CVE-2025-20701 بنفسك باستخدام راديو/دونجل كلاسيكي، لأن هذه الأداة لا تحتوي على ناقل كلاسيكي خاص بها - انظر قسم متطلبات الأجهزة أدناه.
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
يحاول قراءة صفحة فلاش RACE حقيقية واحدة قابلة للقراءة فقط (256 بايت، من عنوان ثابت) للحصول على تأكيد قاطع لـ CVE-2025-20702 - مفيد عندما يجد --race خدمة RACE موجودة ولكنها غير مستجيبة لاستعلامه غير الضار. هذا اختياري ومنفصل عن --race عن قصد: النجاح هنا يسترد محتوى برامج ثابتة حقيقية للجهاز، وليس مجرد إشارة نعم/لا حول ما إذا كانت القناة قابلة للوصول. لا يكتب أبدًا، ولا يمسح، ولا يستخرج مفاتيح الارتباط، ولا يقرأ ذاكرة الوصول العشوائي/السجلات (فقط الفلاش، الذي ليس له آثار جانبية للقراءة) - انظر المرحلة 8 وأقسام خارج النطاق في ROADMAP.md للسبب الكامل. يتطلب تأكيدًا منفصلاً خاصًا به، يصف بالضبط ما يفعله، بالإضافة إلى المطالبة القياسية بملكية الجهاز.
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --json result.json
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --memory-read
يشغل فحوص GATT و RACE والبرامج الثابتة وعنوان BD أعلاه ضد هدف واحد وينتج حكمًا واحدًا: PASS أو PARTIAL أو VULNERABLE أو SUSPECTED_COMPROMISE. يتم طباعة الحكم وكل نتيجة فردية مع تفسير بلغة واضحة بجانب التفاصيل الفنية، بحيث تكون النتيجة قابلة للقراءة دون معرفة عميقة بالبلوتوث - هذه الأداة مخصصة لأي شخص يفحص أجهزته الخاصة، وليس فقط لمتخصصي الأمن. --json يكتب بالإضافة إلى ذلك النتيجة الكاملة (معلومات الجهاز، الحكم وشرحه بلغة واضحة، العلامات مع الأدلة وشرحها بلغة واضحة، وملاحظات المعالجة) إلى ملف.
إضافة --memory-read يدمج تأكيد قراءة الذاكرة في نفس التدقيق والحكم، مع مطالبة تأكيد منفصلة خاصة به أولاً. استعلام عنوان BD يعمل تلقائيًا كجزء من --assess (لا حاجة لعلامة منفصلة، ولا مطالبة تأكيد إضافية) لأنه نفس شكل استعلام البيانات الوصفية منخفض المخاطر مثل فحص البرامج الثابتة.
--assess هو عمدًا لهدف واحد فقط، مثل الفحوص الفردية - لا يوجد وضع "تقييم كل جهاز في النطاق"، لأن ذلك يعني فحصًا نشطًا لأجهزة قد لا تكون ملكك.
buds_audit.py --baseline --target AA:BB:CC:DD:EE:FF
buds_audit.py --check-drift --target AA:BB:CC:DD:EE:FF
--baseline يلتقط لقطة موثوقة لجهاز في المرة الأولى التي تقوم فيها بتقييمه - الهوية (الاسم وبيانات الشركة المصنعة)، جدول GATT، بناء البرامج الثابتة لـ RACE، وحالة الإقران المحلية (قيم منطقية فقط للمقترن/الموثوق/المقترن، أبدًا مواد المفاتيح) - ويخزنها في data/device_baselines.json. لا يتم التقاطها تلقائيًا أبدًا؛ يجب أن تطلبها صراحةً، وتشغيلها مرة أخرى يستبدل خط الأساس الموجود.
--check-drift يلتقط نفس اللقطة مرة أخرى ويقارنها بخط الأساس المخزن، منتجًا حكمًا بناءً على أي انحراف يتم العثور عليه: IDENTITY_DRIFT أو GATT_TABLE_DRIFT أو FIRMWARE_DOWNGRADE أو BOND_STATE_DRIFT. هذا يجيب على "هل تغير شيء منذ أن وثقت بهذا الجهاز آخر مرة؟"، وليس "هل هذا الجهاز عرضة للخطر؟" - إنها إشارة اختراق استدلالية، وليس دليلًا جنائيًا. أي جهاز به أي علامة انحراف يحصل على حكم SUSPECTED_COMPROMISE، والذي يسود على كل شيء آخر.
buds_audit.py --watch
يتمسح باستمرار في نوافذ زمنية ثابتة الطول (Ctrl+C للإيقاف) ويربط كل إعلان يتم رؤيته بالاسم وبيانات الشركة المصنعة. إذا بث عنوانان مختلفان نفس الهوية مع نوافذ مراقبة متداخلة - مما يعني أن كلاهما كان على الهواء بتلك الهوية في نفس الوقت - فإنه يضع علامة POSSIBLE_IMPERSONATION. جهاز فعلي واحد يغير عنوان BLE الخاص به بمرور الوقت (يُرى بالتتابع، وليس بالتزامن) لا يتم وضع علامة عليه؛ فقط جهاز إرسال ثانٍ حقيقي يتم وضع علامة عليه. يتوافق مع الخطوة الأخيرة في نموذج التهديد: انتحال هوية السماعات لهاتف الضحية.
data/affected_devices.json هو كتالوج منسق، وليس قائمة شاملة. مؤكد حاليًا:
| العلامة التجارية | الموديل | معالج Airoha | الثغرات (CVEs) | البرامج الثابتة المصححة |
|---|---|---|---|---|
| Sony | WF-1000XM3 | AB1562 | CVE-2025-20700, CVE-2025-20701, CVE-2025-20702 | لم يُصدر أي إصدار |
وفقًا لإفصاح ERNW، العلامات التجارية الأخرى التي تستخدم معالجات سلسلة Airoha AB1562/AB1565/AB1568 (بما في ذلك Bose وJabra وJBL وMarshall وطرازات Beats قبل التصحيح) يُبلغ أيضًا أنها متأثرة، لكنها ليست في الكتالوج بعد لأن بادئات عنوانها الدقيقة وتفاصيل الشرائح لم يتم تأكيدها بعد مقابل أجهزة حقيقية في هذا المشروع. يمكن فحص جهاز خارج الكتالوج بشكل نشط باستخدام --gatt/--race/--firmware/--assess - الكتالوج يؤثر فقط على تطابق --scan السلبي ووزن الحكم، وليس على ما تختبره الفحوص نفسها.
تقوم هذه الأداة بتقييم CVE-2025-20701 (عدم فرض إقران البلوتوث الكلاسيكي) بشكل سلبي فقط، عبر فحص بناء البرامج الثابتة لـ RACE. الاختبار النشط لمعرفة ما إذا كان يمكن إتمام مصافحة إقران صامتة يتطلب وصول HCI خام عبر Bumble ودونجل بلوتوث USB مخصص متوافق مع Bumble - وهو أمر لا يمكن تحقيقه عبر BlueZ/bleak، ولهذا السبب لا تحاول هذه الأداة ذلك. انظر race-toolkit من ERNW للحصول على تطبيق مرجعي تفاعلي قائم على الدونجل يغطي جميع الثغرات الثلاث.
سلسلة ثغرات SDK الخاصة بـ Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702) تم اكتشافها والإفصاح عنها بواسطة Dennis Heinze و Frieder Steinmetz في ERNW. إن race-toolkit الخاص بهم هو التطبيق المرجعي الذي يملأ هذا المشروع فجوة عدم وجود دونجل بجانبه، ومعرفات UUID الخاصة ببروتوكول RACE لـ GATT وتأطير الحزم المستخدمة هنا تمت قراءتها مباشرة من مصدره بدلاً من تخمينها - انظر core/race.py للتفاصيل. race-toolkit غير مرخص (لا يوجد ملف LICENSE، تم التحقق مباشرة من المستودع) - لا شيء من مصدره يُستخدم هنا خارج الحقائق البروتوكولية الأساسية (معرفات UUID، هيكل البيانات، رموز الأوامر)، والتي تصف بروتوكول Airoha الخاص وليست تعبيرًا أصليًا لمؤلفيه ليتم ترخيصه في المقام الأول.
MIT - انظر LICENSE.
venv/bin/ruff check . --fix && venv/bin/ruff format .
venv/bin/python -m pytest tests/