
منفذ بحث CVE-2026-98260 الخاص بنظام Android والمستهدف لجهاز Xiaomi 17 (pudding)
سلسلة استغلال Android AArch64 لبناء نواة دقيق على Xiaomi 17 (الاسم الرمزي
pudding): بدءًا من ADB shell عادي، يتم على التوالي ضبط SELinux على Permissive، وتصفير UID/GID للعملية الحالية، واستكمال Linux capabilities الكاملة، وتوفير خدمة root خلال دورة التشغيل الحالية.
يستند هذا المشروع ويُعاد نقله من CVE-2026-98260-popsicle-android (Xiaomi 17 Pro Max / popsicle)، وليس بحثًا مستقلًا مُنفَّذًا من الصفر.
execve() في خيط غير رئيسي ينفّذ أولًا de_thread() لتبديل هوية الخيط. إذا فشل exec بعد ذلك (مثلًا عندما يُقاطَع exec_mmap() بـ SIGKILL فيُرجع -EINTR)، فإن المهمة تنتهي قبل تنظيف POSIX timer: يُحرَّر k_itimer، لكن عقدة timerqueue الخاصة به تبقى في شجرة المهمة الحية. وعندما يعيد الأب استرداد تلك المهمة لاحقًا، تعالج cleanup_timerqueue() هذه العقدة المحرَّرة مرة أخرى، ما ينتج عنه use-after-free.
يستغل الاستغلال هذا الـ UAF ليحوّله إلى كتابة صفرية محدودة من ثمانية بايتات، ثم يتقدم على ثلاث مراحل: SELinux ← UID/GID ← capabilities كاملة. للشرح الكامل الموجَّه للمبتدئين راجع docs/VULNERABILITY.md.
| البند | القيمة المقاسة |
|---|---|
| الجهاز | Xiaomi 17، الطراز 25113PN0EC، الاسم الرمزي pudding |
| النظام | Xiaomi HyperOS 4 OS4.0.5.0.XPCCNXM، Android 17 |
| النواة | 6.12.69-android16-6-g43b5ab7e247d-abogki561813157-4k |
| حجم الصفحة | 4 KiB |
| نقطة البداية | ADB shell عادي مصرَّح له، UID 2000، SELinux Enforcing، bootloader مقفل |
| النتيجة الناجحة | UID/GID 0، SELinux Permissive، CapPrm/CapEff/CapBnd=000001ffffffffff |
| دورة الحياة | خدمة root متاحة لأي ADB shell جديد بعد ذلك خلال هذا التشغيل؛ يجب إعادة تشغيلها بعد إعادة التشغيل |
التحقق المستقل بعد النجاح (ADB shell جديد):
uid=0(root) gid=0(root)
CapInh: 000001ffffffffff
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
getenforce: Permissive
هذا ليس root دائمًا. لم تتم الكتابة إلى boot/init_boot أو أي قسم، ويبقى bootloader مقفلًا؛ بعد إعادة التشغيل يعود SELinux إلى وضع Enforcing وتختفي خدمة root.
لا نعد بمعدل نجاح. في الاختبار الفعلي، تم تحقيق تسلسل كامل A→B→C مرة واحدة خلال 9 عمليات إقلاع. شروط النجاح للمراحل الثلاث مستقلة عن بعضها البعض:
| المرحلة | برنامج جهة الجهاز | شرط النجاح |
|---|---|---|
| 1. SELinux | exploit-android-perf | أن يكون getenforce مساويًا لـ Permissive، مع بقاء الحقول الأخرى لكلمة الحالة كما هو متوقع |
| 2. الهوية | exploit-android-perf-chain | أن يتحول UID/GID لنفس العملية إلى 0، ثم exec fullcap |
| 3. الصلاحيات الكاملة | exploit-android-key-ptr + pc98260_rootd.c | capabilities كاملة، ويمكن الوصول إلى خدمة root من shell جديد |
إذا لم تنجح أي خطوة، سيتم إخراج [PARKED] مع الاحتفاظ بالحالة الخطرة، ويجب إعادة التشغيل، ولا يمكن إعادة المحاولة قسرًا خلال نفس عملية الإقلاع.
--aio 16384 هي قيمة الضبط لهذا الجهاز (المرة الناجحة أصابت الصفحة رقم 3903 من صفحات بيانات perf الوصفية).
| الموضع | popsicle | pudding |
|---|---|---|
LINK_SELINUX_STATE | 0xffffffc08289aba0 | 0xffffffc0827f9ba0 |
LINK_GETPID | 0xffffffc080074eac | 0xffffffc080102e10 |
| تسريب عنوان timer | مسبار إرجاع alloc_posix_timer | تم تضمين هذا الرمز، وتم استخدام مدخل posix_cpu_timer_create $arg1 بدلاً منه |
PERF_MAX | 8192 | 16384 (لزيادة فرصة إصابة الصفحة) |
للاطلاع على الأساس والأدلة، راجع docs/ADAPTATION.md.
يلزم وجود Android NDK. تم بناء bin/ في المستودع باستخدام NDK 26.3.11579264 / API 34:
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -NdkHome "$env:LOCALAPPDATA\Android\Sdk\ndk\26.3.11579264" -ApiLevel 34
-VerifyReference يقارن كل ناتج جديد بـ bin/ عبر SHA-256؛ ولن يكونا متطابقين بايتًا ببايت إلا عند استخدام نفس سلسلة الأدوات المستخدمة مع bin/. تغيير إصدار NDK يظل قابلًا للتشغيل، لكن التجزئات ستختلف.
النواتج:
| الناتج | مصدر الإدخال |
|---|---|
exploit-android-perf | src/exploit-android-perf.c |
exploit-android-perf-chain | src/exploit-android-perf-chain.c |
exploit-android-key-ptr | src/exploit-android-key-ptr.c + src/pc98260_rootd.c |
pc98260-su | src/pc98260_su.c |
البناء لا يتصل بالهاتف ولا يكتب إلى الجهاز.
وصّل جهازًا مفعّلًا عليه تصحيح USB ومُصرَّحًا به، وأضف platform-tools إلى PATH:
.\开始98260临时ROOT.bat
أو حدد الجهاز صراحةً:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_98260.ps1 -Serial <ADB序列号> -Reboot
يتحقق سكربت المضيف من طراز الجهاز، وإصدار النظام التراكمي، وسلسلة النواة الكاملة، ومعرّف الإقلاع (boot ID)، وتجزئات (hashes) الملفات الثنائية الأربعة؛ كما يقوم قبل بدء كل مرحلة بضبط /sys/kernel/tracing/tracing_on على 0 (بعض إصدارات HyperOS من miuibpf تُبقيها على 1، فترفض سلسلة الاستغلال العمل). عند ظهور [PARKED] يتوقف السكربت ويطلب إعادة التشغيل.
بعد النجاح يمكن استخدام root على النحو التالي:
adb shell -t /data/local/tmp/pc98260-su
adb shell /data/local/tmp/pc98260-su -c "id"
تتطلب سلسلة الاستغلال استعادة دقيقة لـ"الصفحة التي يوجد فيها المؤقت الهدف". إذا تعذّر ملاحظة نفس الصفحة الهدف، أو كان التحقق بعد كتابة المؤشر غير متسق، يحتفظ البرنامج بالـ zombie/التعيينات ذات الصلة ويتوقف، لتجنب الاستعادة العمياء بينما لا يزال النواة يشير إلى العقدة المعلّقة. في هذه الحالة، ينبغي اعتبار إعادة تشغيل الهاتف من جديد حدًّا للمحاولة التالية.
لا يتضمن هذا المستودع: نسخًا احتياطية للجهاز، أو صور boot/init_boot، أو صور الأقسام، أو payload.bin، أو نواة/ELF مستخرجة من البرنامج الثابت، أو سجلات تشغيل خام، أو أرقام ADB التسلسلية أو boot ID. تم تجاهل logs/ وbuild/ بواسطة Git. لا يوجد في bin/ سوى أربعة ملفات ثنائية مؤقتة لسلسلة رفع الصلاحيات.
/data/local/tmp/pc98260-root.sock؛ يكون الجهاز بأكمله مكشوفًا تمامًا خلال جلسة التشغيل هذه.