
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 هو نسخة متفرعة مجرّدة من استغلال GhostLock من Mobile Hacking Lab، مختزلة إلى بدائية واحدة:
كتابة واحدة مقيّدة عبر futex PI UAF ->
selinux_enforcing = 0.
لا صلاحيات جذر، ولا استبدال لـ 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 (وراثة الأولوية) في نواة لينكس. تتكوّن السلسلة في 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 (Write 1)، وtarget = selinux_enforcing وvalue = base + 0x100، والتي تُرمَّز على هيئة byte0 = 0, byte1 = 1. تكتب النواة 0 في selinux_enforcing[0] — أصبح SELinux الآن في الوضع المتساهل.
ret = 6 (بدلاً من القيمة الافتراضية 9) يؤكد نجاح الكتابة: فقد أصاب المستهلك الهدف أثناء select()، مما أيقظه مبكرًا.
هذا تفرّع من mobilehackinglab/ghostlock-a17 (MIT). ما يلي مأخوذ بنسبة 1:1 من استغلال المنبع:
| المكوّن | الملف | ملاحظات |
|---|---|---|
| KernelSnitch | src/kernelsnitch/* | تسريب mm_struct عبر توقيت تجزئة futex |
| Heap spray | src/spray.c | تخطيط الكائن المزيف، prepare_skb_payload، prepare_kernel_page |
| PI route + 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 الأصلي صلاحيات الجذر الكاملة على 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 من عنصر قائمة انتظار عمل نواة مزيف |
slide.c | تسريب KASLR عبر أوراكل boot_id — غير مطلوب على BZA5، فـ KASLR معطّل |
miniadb.c | الإقلاع عبر ADB TCP |
try_cfi_stage() (في fops.c) | مرحلة configfs المتوافقة مع CFI المستخدمة لإقلاع مسار الجذر |
run_rwforge()، run_bootid_oracle()، rwforge_root_and_capture() | خط أنابيب الجذر بأكمله |
install_embedded_su()، install_embedded_wallpaper() | مساعدات تثبيت الجذر |
Write 2 (cred)، patch_cred_*، patch_task_seccomp | الاستيلاء على بيانات الاعتماد بعد W1 |
يظل GhostLock الأصلي أكثر اكتمالاً وقوة من هذا التفرّع. فـ ghost-hoock ليس بديلاً — بل هو إثبات مفهوم أدنى (PoC) لمهمة واحدة ضيقة: إيقاف SELinux.