Linux arm64 Static Linear Map KASLR Bypass

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

概述

针对 arm64 构建的 Android 内核几乎普遍启用了 CONFIG_ARM64_VA_BITS=39(3 级分页)和 CONFIG_MEMORY_HOTPLUG=y。由于内核虚拟空间仅有 512 GiB 可用,Linux 开发者选择将 linear map 锚定在尽可能低的内核虚拟地址(VA),以便将来热插拔的 RAM 可以向上扩展映射。自从提交 1db780bafa4c 之后,arm64 甚至不再尝试随机化该位置,这意味着:

  • PAGE_OFFSET = 0xffffff8000000000 被编译进内核。
  • PHYS_OFFSET 来源于导出的 memstart_addr,在出厂 Android 设备上实际上是恒定的(今天为 0x80000000)。

因此,每个物理页都有一个确定性的 linear-map 虚拟地址,该地址与 KASLR slide 无关

#define phys_to_virt(p) (((unsigned long)(p) - 0x80000000UL) | 0xffffff8000000000UL)

如果攻击者能够得知或影响某个物理地址(kernel object、PFN 来自 /proc/pagemap,或甚至是 user-controlled page),他们就能立即知道对应的内核虚拟地址,而不会 leaking 随机化的主内核映射。

读取 memstart_addr 并确认变换

memstart_addr/proc/kallsyms 中导出,可在已 root 的设备上读取,或通过任意 kernel-read primitive 读取。Project Zero 使用 Jann Horn 的 tracing-BPF helper (bpf_arb_read) 将其直接转储:

grep memstart /proc/kallsyms
# ... obtains memstart_addr virtual address
./bpf_arb_read <addr_of_memstart_addr> 8

00 00 00 80 00 00 00 00 这些字节确认了 memstart_addr = 0x80000000。一旦 PAGE_OFFSETPHYS_OFFSET 被固定,arm64 linear map 对任意物理地址就是一个静态仿射变换。

Deriving stable .data addresses on devices with a fixed kernel physbase

许多 Pixel 机型仍然在每次启动时将内核解压到 phys_kernel_base = 0x80010000(可在 /proc/iomem 中看到)。将其与静态变换结合,可为任意数据符号得到跨重启稳定的地址:

  1. /proc/kallsyms(或精确的 vmlinux)记录 _stext 和目标符号的随机化内核虚拟地址。
  2. 计算偏移:offset = sym_virt - _stext_virt
  3. 加上静态启动时的 physbase:phys_sym = 0x80010000 + offset
  4. 转换为 linear-map VA:virt_sym = phys_to_virt(phys_sym)

示例(在 Pixel 9 上的 modprobe_path):offset = 0x1fe2398phys = 0x81ff2398virt = 0xffffff8001ff2398。多次重启后,bpf_arb_read 0xffffff8001ff2398 返回相同字节,因此利用载荷可以将 0xffffff8000010000 视为所有 .data 偏移的合成、非随机基址。

该映射为 RW,因此任何能将攻击者数据放入内核虚拟地址空间的原语(double free、UAF、non-paged heap write 等)都可以在不泄露真实 KASLR slide 的情况下修补 credentials、LSM hooks 或 dispatch tables。唯一的限制是 .text 在 linear map 中被映射为 non-executable,因此 gadget hunting 仍然需要传统的 leak。

PFN spraying when the kernel physbase is randomized

例如 Samsung 等厂商会随机化内核加载 PFN,但静态 linear map 仍可被滥用,因为 PFN 分配并非完全随机:

  1. Spray user pagesmmap() 大约 5 GiB,触碰每页以触发页面错误将其换入。
  2. Harvest PFNs:读取每页的 /proc/pagemap(或使用其他 PFN leak)以收集 backing PFN 列表。
  3. Repeat and profile:重启,重复运行 100×,构建直方图显示每个 PFN 被攻击者控制的频率。有些 PFN 非常热点(在启动后短时间内 100/100 次都被分配)。
  4. Convert PFN → kernel VA
  • phys = (pfn << PAGE_SHIFT) + offset_in_page
  • virt = phys_to_virt(phys)
  1. Forge kernel objects in those pages 并将受害者指针(UAF、overflow 等)引导到已知的 linear-map 地址。

因为 linear map 是 identity-mapped 的 RW 内存,这项技术允许你在真实内核基址移动时,仍能在确定性的 kernel VAs 上放置完全由攻击者控制的数据。利用可以在喷射的页面内预构造伪造的 file_operationscred 或 refcount 结构,然后将现有的内核指针 pivot 到这些结构上。

Practical workflow for arm64 Android exploits

  1. Info gathering
  • 使用 root 或内核读原语从 /proc/kallsyms 导出 memstart_addr_stext 和目标符号。
  • 在 Pixel 设备上,从 /proc/iomem 信任静态 physbase;在其他设备上,准备 PFN profiler。
  1. Address calculation
  • 应用上面的偏移计算,并将得到的 linear-map VAs 缓存到利用中。
  • 对于 PFN spraying,保留一个反复落入攻击者内存的“可靠” PFN 列表。
  1. Exploit integration
  • 当可用 arbitrary write 时,直接在预计算地址处修补目标(如 modprobe_pathinit_cred 或 security ops 数组)。
  • 当只有堆损坏时,在已知受控的页面中制作伪造对象,并将受害者指针重定向到那些 linear-map VA。
  1. Verification
  • 在进行破坏性写入之前,使用 bpf_arb_read 或任何安全的读原语对计算出的地址进行合理性检查,确认其包含预期字节。

该工作流程消除了 Android 上面向数据的内核利用中的 KASLR-leak 阶段,从而显著降低了利用复杂度并提高了可靠性。

参考资料

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks