
GhostLock مُجرَّد إلى عنصر أساسي واحد: إيقاف SELinux على Galaxy A17 (BZA5) عبر futex PI UAF (CVE-2026-43499). بدون root، بدون cred patch، بدون rwforge.
نسخة معدلة بسيطة من GhostLock تحتفظ ببدائية واحدة فقط: إيقاف SELinux عبر CVE-2026-43499 (futex PI UAF).
ghost-hoock يعمل على Samsung A17
ghost-hoock هو نسخة معدلة مجردة من استغلال GhostLock بواسطة Mobile Hacking Lab، مختصرة إلى بدائية واحدة:
كتابة واحدة مقيّدة عبر futex PI UAF ->
selinux_enforcing = 0.
لا root، لا استبدال cred، لا قناة rwforge، لا UMH، لا configfs. فقط الحد الأدنى المطلوب لتحويل SELinux إلى الوضع المتساهل على النواة المصابة.
مثال على المخرجات على Samsung Galaxy A17 (SM-A175F, BZA5):
[] kernel: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[+] offsets matched: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[] init_cred image=ffffffc082512b08 alias=ffffff8002512b08
[+] startup context pid=10331 uid=2000 euid=2000 gid=2000 egid=2000 attr=u:r:shell:s0 enforce=1
[+] startup limits pid=10331 NoNewPrivs=0 Seccomp=0 Seccomp_filters=0
[+] build config pid=10331 label=ghost-hoock
[] p0 kernel_phys_load=0000000040000000 delta=0000000000000000 core=0
[] target selinux_enforcing=ffffff800277e560
[] W1 attempt 1/20
[] === W1: SELinux === target=0xffffff800277e560 mode=1
[] prepare_kernel_page ok attempt=1
[] pselect route setup simple=0 shift=0 page=ffffff806c4f0000 fake_lock=ffffff806c4f0000 ...
[] pselect returned ret=6 errno=0 calls=1 success=1 delay=0
[] pselect route done calls=1 success=1 step=0 errno=0
[+] SELinux DISABLED (attempt 1)
ثم:
$ getenforce
Permissive
getenforce يعيد Permissive
يستهدف الاستغلال CVE-2026-43499 — استخدام بعد التحرير (use-after-free) في سلسلة rt_mutex الخاصة بـ futex PI (وراثة الأولوية) في نواة Linux. السلسلة في ghost-hoock تتكون من أربع خطوات:
قناة جانبية للتوقيت ضد جدول تجزئة futex في النواة. نقوم بضرب FUTEX_WAKE_PRIVATE على مجموعة من futexes في مساحة المستخدم، ونقيس فروقات rdtsc، ونربط تصادمات دلاء التجزئة. هذا يستعيد عنوان mm_struct الخاص بنا — قاعدة صفحة الرش التي نحتاجها لاحقًا.
هذه تقنية KernelSnitch، مأخوذة حرفيًا من الاستغلال الأصلي.
نخصص صفحة slab كبيرة من رتبة order-3، ثم نرتبها بتخطيط الكائنات المزيفة المستخدم في مسار PI:
| الإزاحة | الكائن | الغرض |
|---|---|---|
0x0E80 | fake_lock | rt_mutex مزيف |
0x0F80 | fake_fops | جدول file_operations مزيف |
0x1180 | fake_w0 | rt_mutex_waiter مزيف يُستخدم كشجرة هدف |
0x1240 | fake_right | عقدة يمين مزيفة في شجرة rb — من هنا تأتي قيمة الكتابة |
0x1260 | fake_left | عقدة يسار مزيفة في شجرة rb |
0x1280 | fake_task | task_struct مزيف |
تُرسل الصفحة بأكملها عبر مقبس AF_UNIX بحجم SKB_SEND_SIZE = 2 * ORDER3_SIZE من sendmsg، بحيث تهبط بيانات skb على صفحتنا المسرّبة. ثم نحررها بترتيب محكوم بحيث تنتهي صفحتنا على slab جزئي لكل وحدة معالجة يمكننا استعادته.
ثلاثة خيوط:
FUTEX_WAIT_REQUEUE_PI على f_wait، مستهدفًا f_pi_target.FUTEX_LOCK_PI على f_pi_target ثم على f_pi_chain.sched_setattr(tid, SCHED_BATCH, nice=19) على TID الخاص بالـ waiter، مما يفعّل rt_mutex_setprio() ويجبر النواة على السير في شجرة PI المزيفة.نداء رابع من الخيط الرئيسي — FUTEX_CMP_REQUEUE_PI(1, f_pi_target) — يبدأ عملية إعادة الطابور. داخل النواة، يعمل rb_erase() على شجرتنا المزيفة.
ينسخ pselect() / select() قيمة fd_set الخاصة بالمستخدم إلى مكدس النواة ثم يسير عليها لاحقًا. نرتب خرائط بتات fd_set بحيث تهبط الكلمات التي تعاملها النواة كمؤشرات شجرة rb على fake_right وأصلها — وتصبح النتيجة rb_set_parent(child, parent):
*(uint64_t *)target = value | color
بالنسبة لـ mode = 1 (كتابة 1)، target = selinux_enforcing و value = base + 0x100، والتي تُرمّز كـ byte0 = 0, byte1 = 1. تكتب النواة 0 في selinux_enforcing[0] — أصبح SELinux الآن متساهلًا.
ret = 6 (بدلًا من الافتراضي 9) يؤكد أن الكتابة قد هبطت: أصاب الـ consumer الهدف أثناء select()، مما أيقظه مبكرًا.
هذه نسخة معدلة من mobilehackinglab/ghostlock-a17 (MIT). ما يلي مأخوذ 1:1 من الاستغلال الأصلي:
| المكوّن | الملف | ملاحظات |
|---|---|---|
| KernelSnitch | src/kernelsnitch/* | تسريب mm_struct عبر توقيت تجزئة futex |
| رش الكومة | src/spray.c | تخطيط الكائنات المزيفة، prepare_skb_payload، prepare_kernel_page |
| مسار PI + pselect | src/route.c | prepare_pselect_fdsets، do_pselect_fake_lock_route، consumer_thread، waiter_thread، owner_thread |
| إزاحات BZA5 | include/offsets_bza5.h | جدول الرموز المستخرج من 6.12.23-android16-5-abA175FXXS5BZD2-4k |
| ترويسة هدف BZA5 | include/target.h | تخطيط العناوين، إزاحات الحمولة (مجموعة فرعية W1 فقط) |
| إزاحات البنية وقت التشغيل | include/runtime_struct_offsets.h | وحدات ماكرو _RSO() لحقول task_struct |
الكود المساعد (وحدات ماكرو pr_*، SYSCHK، pin_to_core، set_limit، set_unbuffer) محتفظ به أيضًا كما هو من الأصل.
يحقق GhostLock الأصلي root كامل على A17: يثبّت قناة قراءة/كتابة فيزيائية rwforge، ويعدّل cred / real_cred، ويشغّل مساعد UMH بصلاحيات init، ويلتقط السجلات، والمزيد. في ghost-hoock، كل ما بعد الكتابة المقيّدة الأولى قد اختفى.
| الملف المُزال | لماذا كان موجودًا في الأصل |
|---|---|
rwforge_a17.c | قناة قراءة/كتابة فيزيائية Marching-forger عبر pipe_buffers |
pipe_physrw.c, pipe_reclaim.c | استعادة مخزن الأنابيب -> قراءة/كتابة عشوائية في النواة |
root.c | استبدال cred / real_cred، تثبيت su، ترقيع SELinux SID |
umh_root.c, wq_umh_root() (في main.c) | تشغيل مساعد بصلاحيات init من عنصر workqueue مزيف في النواة |
slide.c | تسريب KASLR عبر أوراكل boot_id — غير مطلوب على BZA5، KASLR معطّل |
miniadb.c | التمهيد عبر ADB TCP |
try_cfi_stage() (في fops.c) | مرحلة configfs متوافقة مع CFI تُستخدم لتمهيد مسار root |
run_rwforge(), run_bootid_oracle(), rwforge_root_and_capture() | خط أنابيب root بأكمله |
install_embedded_su(), install_embedded_wallpaper() | مساعدات تثبيت root |
الكتابة 2 (cred)، patch_cred_*، patch_task_seccomp | الاستيلاء على بيانات الاعتماد بعد W1 |
يبقى GhostLock الأصلي أكثر اكتمالًا وقوة من هذه النسخة المعدلة. ghost-hoock ليس بديلًا — إنه PoC بسيط لمهمة واحدة ضيقة: إيقاف SELinux.
يتطلب clang و make في $PATH. تم اختباره على Termux؛ يعمل أيضًا عبر adb shell إذا كانت سلسلة الأدوات متوفرة.
git clone https://github.com/genksome/ghost-hoock
cd ghost-hoock
make
المخرجات: ./ghost-hoock (aarch64, PIE).
عبر Android NDK (على جهاز كمبيوتر)
make NDK=/path/to/android-ndk-r26
أو يدويًا: