mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Translated ['src/linux-hardening/privilege-escalation/README.md', 'src/b
This commit is contained in:
		
							parent
							
								
									8c755f2d5d
								
							
						
					
					
						commit
						867f71aff1
					
				| @ -1,99 +1,99 @@ | ||||
| # iOS Physical Use-After-Free via IOSurface | ||||
| # iOS Physical Use After Free via IOSurface | ||||
| 
 | ||||
| {{#include ../../banners/hacktricks-training.md}} | ||||
| 
 | ||||
| 
 | ||||
| ## Physical use-after-free | ||||
| 
 | ||||
| 这是对文章 [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) 的总结,更多使用该技术的利用信息可以在 [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) 找到。 | ||||
| 这是对帖子 [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) 的摘要,更多关于使用该技术的利用细节可见 [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) | ||||
| 
 | ||||
| ### Memory management in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a> | ||||
| 
 | ||||
| iOS 上用户进程的虚拟内存地址空间从 **0x0 到 0x8000000000**。但是这些地址并不直接映射到物理内存。相反,内核使用 **page tables** 来将虚拟地址转换为实际的 **physical addresses**。 | ||||
| 用户进程在 iOS 上的虚拟内存地址空间范围为 0x0 到 0x8000000000。但这些地址并不直接映射到物理内存。相反,内核使用页表将虚拟地址翻译为实际的物理地址。 | ||||
| 
 | ||||
| #### Levels of Page Tables in iOS | ||||
| 
 | ||||
| 页表以三级层次结构组织: | ||||
| 页表在 iOS 中按层次组织为三层: | ||||
| 
 | ||||
| 1. **L1 Page Table (Level 1)**: | ||||
| * 此处的每个条目代表一大块虚拟内存区域。 | ||||
| * 覆盖 **0x1000000000 bytes**(或 **256 GB**)的虚拟内存。 | ||||
| 2. **L2 Page Table (Level 2)**: | ||||
| * 此处的每个条目代表比 L1 更小的虚拟内存区域,具体为 **0x2000000 bytes**(32 MB)。 | ||||
| * 如果 L1 条目无法自行映射整个区域,它可能指向一个 L2 表。 | ||||
| 3. **L3 Page Table (Level 3)**: | ||||
| * 这是最细粒度的级别,每个条目映射一个 **4 KB** 的内存页。 | ||||
| * 如果需要更细的控制,L2 条目可能指向 L3 表。 | ||||
| 1. **L1 Page Table (Level 1)**: | ||||
| * 此层的每个条目表示一大段虚拟内存。 | ||||
| * 它覆盖 **0x1000000000 bytes**(或 **256 GB**)的虚拟内存。 | ||||
| 2. **L2 Page Table (Level 2)**: | ||||
| * 这里的每个条目表示更小的虚拟内存区域,具体为 **0x2000000 bytes**(32 MB)。 | ||||
| * 如果 L1 条目无法自身映射整个区域,它可能指向一个 L2 表。 | ||||
| 3. **L3 Page Table (Level 3)**: | ||||
| * 这是最细的级别,每个条目映射一个 **4 KB** 的内存页。 | ||||
| * 如果需要更精细的控制,L2 条目可能指向一个 L3 表。 | ||||
| 
 | ||||
| #### Mapping Virtual to Physical Memory | ||||
| 
 | ||||
| * **Direct Mapping (Block Mapping)**: | ||||
|   * 页表中的某些条目直接将一段虚拟地址范围映射到一段连续的物理地址(类似捷径)。 | ||||
| * **Pointer to Child Page Table**: | ||||
|   * 如果需要更细的控制,一个级别(例如 L1)中的条目可以指向下一级的 **child page table**(例如 L2)。 | ||||
| * **Direct Mapping (Block Mapping)**: | ||||
| * 页表中的某些条目直接将一段虚拟地址范围映射到连续的物理地址范围(类似捷径)。 | ||||
| * **Pointer to Child Page Table**: | ||||
| * 如果需要更精细的控制,某一层(例如 L1)中的条目可以指向下一层的子页表(例如 L2)。 | ||||
| 
 | ||||
| #### Example: Mapping a Virtual Address | ||||
| 
 | ||||
| 假设你尝试访问虚拟地址 **0x1000000000**: | ||||
| 
 | ||||
| 1. **L1 Table**: | ||||
| * 内核检查与该虚拟地址对应的 L1 页表条目。如果它包含一个指向 L2 page table 的指针,就转到该 L2 表。 | ||||
| 2. **L2 Table**: | ||||
| * 内核检查 L2 页表以获得更详细的映射。如果该条目指向 L3 page table,就继续到 L3。 | ||||
| 3. **L3 Table**: | ||||
| * 内核查找最终的 L3 条目,它指向实际内存页的 **物理地址**。 | ||||
| 1. **L1 Table**: | ||||
| * 内核检查对应该虚拟地址的 L1 页表条目。如果它包含指向 L2 页表的指针,则转到该 L2 表。 | ||||
| 2. **L2 Table**: | ||||
| * 内核在 L2 页表中查找更详细的映射。如果该条目指向 L3 页表,则继续到 L3。 | ||||
| 3. **L3 Table**: | ||||
| * 内核查找最终的 L3 条目,该条目指向实际内存页的物理地址。 | ||||
| 
 | ||||
| #### Example of Address Mapping | ||||
| 
 | ||||
| 如果你在 L2 表的第一个索引写入物理地址 **0x800004000**,那么: | ||||
| 
 | ||||
| * 虚拟地址从 **0x1000000000** 到 **0x1002000000** 映射到物理地址从 **0x800004000** 到 **0x802004000**。 | ||||
| * 这是在 L2 级别的 **block mapping**。 | ||||
| * 虚拟地址从 **0x1000000000** 到 **0x1002000000** 将映射到物理地址从 **0x800004000** 到 **0x802004000**。 | ||||
| * 这是 L2 级别的 **block mapping**。 | ||||
| 
 | ||||
| 或者,如果 L2 条目指向一个 L3 表: | ||||
| 或者,如果 L2 条目指向 L3 表: | ||||
| 
 | ||||
| * 虚拟地址范围 **0x1000000000 -> 0x1002000000** 中的每个 4 KB 页面将由 L3 表中的单独条目进行映射。 | ||||
| * 虚拟地址范围 **0x1000000000 -> 0x1002000000** 中的每个 4 KB 页面将由 L3 表中的单独条目映射。 | ||||
| 
 | ||||
| ### Physical use-after-free | ||||
| 
 | ||||
| 当发生一个 **physical use-after-free (UAF)** 时,情况如下: | ||||
| 当发生 **physical use-after-free (UAF)** 时,流程通常为: | ||||
| 
 | ||||
| 1. 进程分配了一段可读写的内存。 | ||||
| 2. 内核更新 page tables,将这段内存映射到进程可访问的特定物理地址。 | ||||
| 1. 进程分配了一块可读写的内存。 | ||||
| 2. 页表被更新以将这块内存映射到进程可访问的特定物理地址。 | ||||
| 3. 进程释放(deallocate)了该内存。 | ||||
| 4. 但是由于一个 **bug**,内核**忘记从页表中移除该映射**,尽管它将对应的物理内存标记为可重用。 | ||||
| 5. 内核随后可能将这块“已释放”的物理内存**重新分配**给其他用途,比如内核数据。 | ||||
| 6. 由于映射未被移除,进程仍然可以对这段物理内存进行**读写**。 | ||||
| 4. 但是,由于一个 bug,内核忘记从页表中移除该映射,即便内核将对应的物理内存标记为已释放。 | ||||
| 5. 内核随后可能将这块“已释放”的物理内存重新分配给其他用途,例如内核数据结构。 | ||||
| 6. 由于映射未被移除,进程仍然可以对这块物理内存进行读写。 | ||||
| 
 | ||||
| 这意味着进程可能访问到**内核内存页**,其中可能包含敏感数据或结构,攻击者可能借此**操纵内核内存**。 | ||||
| 这意味着进程可以访问到内核内存页,这些页可能包含敏感数据或结构,从而可能允许攻击者操纵内核内存。 | ||||
| 
 | ||||
| ### IOSurface Heap Spray | ||||
| 
 | ||||
| 由于攻击者无法控制具体哪些内核页会被分配给被释放的内存,他们使用一种叫做 **heap spray** 的技术: | ||||
| 因为攻击者无法控制哪些具体的内核页会被分配给被释放的内存,他们使用一种称为 **heap spray** 的技术: | ||||
| 
 | ||||
| 1. 攻击者在内核内存中创建大量 IOSurface objects。 | ||||
| 2. 每个 IOSurface 对象在其某个字段中包含一个用于识别的 **magic value**,便于检测。 | ||||
| 3. 他们扫描那些已释放的页面,查看是否有这些 IOSurface 对象落在被释放的页面上。 | ||||
| 4. 一旦发现某个 IOSurface 对象位于被释放页面上,就可以利用它来**读写内核内存**。 | ||||
| 1. 攻击者在内核内存中创建大量的 IOSurface 对象。 | ||||
| 2. 每个 IOSurface 对象在其某个字段中包含一个用于识别的 **magic value**,便于定位。 | ||||
| 3. 他们扫描被释放的页以查看是否有任何这些 IOSurface 对象被分配到了被释放的页上。 | ||||
| 4. 一旦发现某个 IOSurface 对象落在被释放的页上,就可以利用它来读写内核内存。 | ||||
| 
 | ||||
| 更多信息见 [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups) | ||||
| 更多信息参见 [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups) | ||||
| 
 | ||||
| > [!TIP] | ||||
| > 注意,iOS 16+(A12+)设备引入了硬件缓解手段(例如 PPL 或 SPTM),这些措施使得 physical UAF 技术的可行性大大降低。 | ||||
| > PPL 对与代码签名、权限(entitlements)和敏感内核数据相关的页面实施严格的 MMU 保护,因此即便某页被重用,从 userland 或被破坏的内核代码对受 PPL 保护页面的写入也会被阻止。 | ||||
| > Secure Page Table Monitor (SPTM) 通过加强页表更新本身来扩展 PPL。它确保即使是高权限的内核代码也无法在不经过安全检查的情况下悄然重新映射已释放页面或篡改映射。 | ||||
| > KTRR (Kernel Text Read-Only Region) 在启动后将内核的代码段锁定为只读。这阻止了对内核代码的运行时修改,封堵了 physical UAF 利用常依赖的一个主要攻击路径。 | ||||
| > 另外,IOSurface 的分配变得更不可预测且更难映射回用户可访问区域,这使得“magic value 扫描”技巧变得不可靠。并且 IOSurface 现在受 entitlements 和 sandbox 限制的保护。 | ||||
| > 注意,iOS 16+(A12+)设备引入了硬件缓解措施(如 PPL 或 SPTM),这使得 physical UAF 技术变得远不那么可行。   | ||||
| > PPL 在与代码签名、权限以及敏感内核数据相关的页面上强制执行严格的 MMU 保护,因此即便某个页面被重用,来自 userland 或被攻破的内核代码对 PPL 保护页面的写入也会被阻止。   | ||||
| > Secure Page Table Monitor (SPTM) 通过强化页表更新本身来扩展 PPL。它确保即便是特权内核代码也不能在不经过安全检查的情况下悄然重新映射已释放页面或篡改映射。   | ||||
| > KTRR (Kernel Text Read-Only Region) 在引导后将内核的代码段锁为只读。这阻止了对内核代码的任何运行时修改,封堵了许多 physical UAF 利用常依赖的攻击向量。   | ||||
| > 此外,`IOSurface` 的分配变得不那么可预测,也更难映射到用户可访问的区域,这使得“magic value 扫描”策略的可靠性大幅下降。并且 `IOSurface` 现在受到权限(entitlements)和沙箱限制的保护。 | ||||
| 
 | ||||
| ### Step-by-Step Heap Spray Process | ||||
| 
 | ||||
| 1. **Spray IOSurface Objects**: 攻击者创建大量带有特殊标识(“magic value”)的 IOSurface objects。 | ||||
| 2. **Scan Freed Pages**: 他们检查是否有对象被分配到已释放的页面上。 | ||||
| 3. **Read/Write Kernel Memory**: 通过操纵 IOSurface 对象中的字段,他们获得了在内核内存中进行**任意读写**的能力。这使他们能够: | ||||
| * 使用一个字段来**读取内核内存中的任意 32-bit 值**。 | ||||
| * 使用另一个字段来**写入 64-bit 值**,从而实现稳定的 **kernel read/write primitive**。 | ||||
| 1. **Spray IOSurface Objects**: 攻击者创建大量包含特殊标识(“magic value”)的 IOSurface 对象。 | ||||
| 2. **Scan Freed Pages**: 检查这些对象是否有被分配到被释放的页面上。 | ||||
| 3. **Read/Write Kernel Memory**: 通过操纵 IOSurface 对象中的字段,他们获得了在内核内存中进行**任意读写**的能力。这使得他们可以: | ||||
| * 使用一个字段来**读取内核内存中的任意 32 位值**。 | ||||
| * 使用另一个字段来**写入 64 位值**,从而实现稳定的**内核读/写原语**。 | ||||
| 
 | ||||
| Generate IOSurface objects with the magic value IOSURFACE_MAGIC to later search for: | ||||
| Generate IOSurface objects with the magic value IOSURFACE\_MAGIC to later search for: | ||||
| ```c | ||||
| void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) { | ||||
| if (*nClients >= 0x4000) return; | ||||
| @ -114,7 +114,7 @@ io_connect_t id = result.surface_id; | ||||
| } | ||||
| } | ||||
| ``` | ||||
| 在一个已释放的物理页面中搜索 **`IOSurface`** 对象: | ||||
| 在一个已释放的物理页面中搜索 **`IOSurface`** 对象: | ||||
| ```c | ||||
| int iosurface_krw(io_connect_t client, uint64_t *puafPages, int nPages, uint64_t *self_task, uint64_t *puafPage) { | ||||
| io_connect_t *surfaceIDs = malloc(sizeof(io_connect_t) * 0x4000); | ||||
| @ -148,24 +148,24 @@ free(surfaceIDs); | ||||
| return 0; | ||||
| } | ||||
| ``` | ||||
| ### 使用 IOSurface 实现内核读/写 | ||||
| ### 使用 IOSurface 实现 Kernel Read/Write | ||||
| 
 | ||||
| 在获得对内核内的 IOSurface 对象的控制后(映射到可从用户态访问的已释放物理页面),我们可以用它进行 **任意内核读写操作**。 | ||||
| 在对 kernel memory 中的 IOSurface 对象取得控制(映射到可从 userspace 访问的已释放物理页面)之后,我们可以使用它进行 **arbitrary kernel read and write operations**。 | ||||
| 
 | ||||
| **IOSurface 的关键字段** | ||||
| 
 | ||||
| IOSurface 对象有两个关键字段: | ||||
| 
 | ||||
| 1. **Use Count Pointer**:允许 **32-bit 读取**。 | ||||
| 2. **Indexed Timestamp Pointer**:允许 **64-bit 写入**。 | ||||
| 1. **Use Count Pointer**:允许 **32-bit read**。 | ||||
| 2. **Indexed Timestamp Pointer**:允许 **64-bit write**。 | ||||
| 
 | ||||
| 通过覆写这些指针,可以将它们重定向到内核内的任意地址,从而实现读/写能力。 | ||||
| 通过覆盖这些指针,我们可以将它们重定向到 kernel memory 中的任意地址,从而实现读/写能力。 | ||||
| 
 | ||||
| #### 32-Bit 内核读取 | ||||
| #### 32-Bit Kernel Read | ||||
| 
 | ||||
| 要执行一次读取: | ||||
| 要执行读取: | ||||
| 
 | ||||
| 1. 将 **use count pointer** 覆写为指向目标地址减去 0x14 字节偏移的位置。 | ||||
| 1. 覆盖 **use count pointer**,使其指向目标地址减去 0x14 字节的偏移。 | ||||
| 2. 使用 `get_use_count` 方法读取该地址处的值。 | ||||
| ```c | ||||
| uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) { | ||||
| @ -186,9 +186,9 @@ return value; | ||||
| ``` | ||||
| #### 64 位内核写入 | ||||
| 
 | ||||
| 要执行写入: | ||||
| 要执行写入操作: | ||||
| 
 | ||||
| 1. 将 **索引时间戳指针** 覆盖为目标地址。 | ||||
| 1. 将 **indexed timestamp pointer** 覆盖为目标地址。 | ||||
| 2. 使用 `set_indexed_timestamp` 方法写入一个 64 位值。 | ||||
| ```c | ||||
| void set_indexed_timestamp(io_connect_t client, uint32_t surfaceID, uint64_t value) { | ||||
| @ -205,11 +205,11 @@ iosurface_set_indexed_timestamp_pointer(info.object, orig); | ||||
| ``` | ||||
| #### 利用流程回顾 | ||||
| 
 | ||||
| 1. **Trigger Physical Use-After-Free**:释放的页面可被重用。 | ||||
| 2. **Spray IOSurface Objects**:在内核内存中分配许多带有唯一 "magic value" 的 IOSurface 对象。 | ||||
| 3. **Identify Accessible IOSurface**:定位位于你控制的已释放页面上的 IOSurface。 | ||||
| 4. **Abuse Use-After-Free**:修改 IOSurface 对象中的指针,通过 IOSurface 方法实现任意 **kernel read/write**。 | ||||
| 1. **Trigger Physical Use-After-Free**: 已释放的页面可被重新使用。 | ||||
| 2. **Spray IOSurface Objects**: 在 kernel memory 中分配大量带有唯一 "magic value" 的 IOSurface 对象。 | ||||
| 3. **Identify Accessible IOSurface**: 在你控制的已释放页面上定位一个 IOSurface。 | ||||
| 4. **Abuse Use-After-Free**: 修改 IOSurface 对象中的指针,通过 IOSurface 方法实现任意 **kernel read/write**。 | ||||
| 
 | ||||
| 利用这些原语,漏洞利用能够对内核内存进行可控的 **32-bit reads** 和 **64-bit writes**。后续的 jailbreak 步骤可能需要更稳定的 read/write primitives,这可能要求绕过额外的保护(例如在较新的 arm64e 设备上的 PPL)。 | ||||
| 利用这些原语,漏洞利用可以对 kernel memory 进行受控的 **32-bit reads** 和 **64-bit writes**。后续的 jailbreak 步骤可能涉及更稳定的 read/write primitives,这可能需要绕过额外的保护(例如针对较新的 arm64e 设备的 PPL)。 | ||||
| 
 | ||||
| {{#include ../../banners/hacktricks-training.md}} | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user