Suzu macOS Playground

M13 · 階梯 4 · L4

漏洞模型與緩解

用抽象記憶體模型把「記憶體破壞類別、邏輯漏洞、TOCTOU、LPE/sandbox-escape 鏈的結構」講成一套防禦心智模型——重點是 mitigation(PAC、KASLR、PPL、SPTM/TXM、kalloc_type、zone sequestering)為何有效、攻擊者在每道緩解前被迫怎麼轉向(直到只剩 data-only)、以及近年 CVE 的根因類別。全章只談類別/根因/防禦,絕不提供 offset/gadget/payload/可運行 PoC;所有模型皆為教學抽象,不等於真實可利用性。

● 前置:M01、M02、M09、M12 otool · vmmap · sysctl · machine · sw_vers · codesign

進入前:合法授權範圍與模型邊界聲明

法律與倫理脈絡微課(coordinated disclosure)

在碰任何漏洞「機制」之前,先把三件事釘在腦裡:

  • 負責任揭露(coordinated disclosure):發現漏洞後,先私下通報廠商(Apple Security Research / product-security@apple.com、Apple Security Bounty),給合理修補期,再公開。公開的是根因與防禦教訓,不是 victim 能直接套用的武器。
  • 研究與未授權存取的法律界線:在不屬於你、也未獲授權的系統上測試漏洞,多數司法管轄區屬刑事犯罪(未授權存取電腦)。「我只是研究」不是免責事由。界線是授權,不是動機。
  • 「理解防護模型以評估影響」≠「提供攻擊腳本」:本章教你能說出「這類 bug 能不能被串成提權、為什麼某道緩解讓它變難」。這是評估與防禦能力。它與「給一份能跑的 exploit」本質不同——前者讓防禦者排優先序與補洞,後者只降低攻擊門檻。本章只做前者。

為什麼「攻防頂點」是純概念的

前面你學了每一道閘(M03–M09)怎麼擋人、逆向工具鏈(M12)怎麼看清一個二進位在做什麼。這一章把鏡頭拉到攻擊者要怎麼把一個 bug 變成「能跨越信任邊界的能力」——但我們只看結構,不看實作。

理由很簡單:真實可利用性是「軍備競賽」的瞬時快照。一個記憶體破壞 bug 能不能被利用,取決於當下有哪些緩解、有沒有可配的資訊洩漏、目標物件的佈局、以及攻擊者願意投入多少工程。本章的價值不是教你跨過去,而是讓你能對任一 bug 類別說出:它屬於哪一類根因、現代緩解在哪一環卡住它、攻擊者因此被迫轉向什麼、防禦/偵測該下在哪。

漏洞的類別與根因(不是 offset,是分類)

把漏洞先分類,比記任何單一案例都重要。防禦的第一步是「這是哪一類 bug」,因為同一類有共通的根因與共通的緩解。

類別根因(一句話)防禦/緩解方向
記憶體破壞 — 空間性存取越過物件邊界(OOB read/write)bounds 資訊、type-aware 配置、隔離相鄰物件
記憶體破壞 — 時間性用了已釋放的物件(UAF)、二次釋放(double free)配置器型別隔離、延遲重用、指標完整性
型別混淆(type confusion)把物件當成另一型別來解讀強型別檢查、型別隔離 zone
整數問題溢位/截斷導致配置或長度計算錯誤飽和運算、配置前驗算、編譯期檢查
未初始化記憶體讀到上一個使用者留下的內容(資訊洩漏)配置歸零、結構化清理
邏輯漏洞程式照寫的邏輯走,但邏輯本身允許了不該允許的事不變式檢查、最小權限、責任驗證(接 M08 audit token)
TOCTOU(time-of-check/time-of-use)檢查與使用之間狀態被換掉(競態)原子化檢查+使用、用 fd 而非路徑、避免可被換的中介

TOCTOU 與邏輯漏洞:不碰記憶體也能破信任

不是所有漏洞都跟記憶體破壞有關。邏輯漏洞與 TOCTOU 常常是「程式碼完全照寫的跑,但設計允許了不該允許的狀態」:

  • TOCTOU:服務先檢查「呼叫者有權限/這個路徑安全」,過一會兒才真正使用。攻擊者在這個時間窗把被檢查的東西換掉(典型如把路徑指向別處、把已驗證的物件抽換)。這直接呼應 M08:用 audit token 而非 PID 驗證責任,正是為了壓掉「檢查時是 A、使用時變 B」的窗。
  • 邏輯漏洞:例如把信任建立在可被呼叫方偽造的資訊上(PID 可重用、路徑可符號連結)。防禦是把信任錨定在不可偽造、與這次請求綁定的憑證上。

鏈的「結構」:LPE 與 sandbox escape 為什麼要串

單一 bug 很少直接給你「想要的能力」。現代 macOS 把權力切成很多層(M09),所以攻擊者要的是一條鏈——每一步只跨一道邊界,把上一步的小能力換成下一步的前提。本章只講這條鏈長什麼形狀、每一節靠哪道緩解擋住,不講任何一節怎麼做。

(教學抽象示意,非可執行流程,非真實順序保證)

  [初始執行]  app 內被誘導執行攻擊者控制的邏輯
       │  受限於 ── App Sandbox(M07):能碰的 syscall/檔案/服務被 profile 砍掉
       ▼
  [sandbox escape]  找一個「沙箱內可達、但實作在沙箱外」的介面的 bug
       │  受限於 ── XPC 責任驗證(M08)、entitlement 檢查(M03)、MACF policy
       ▼
  [本機提權 LPE]  從低權使用者態 → 更高權限(service / root / 核心態)
       │  受限於 ── 權限分層(M09)、PAC、KASLR、PPL / SPTM·TXM、kalloc_type…
       ▼
  [目標能力]  讀寫他人資料 / 持久化 / 關閉防護

下面用一個抽象模型走 UAF 的「形狀」(dangling 指標 → 同塊重用 → 透過舊指標操作新物件),並開關 arm64e PAC 看 mitigation 如何抬高成本——務必記得面板的免責:教學抽象 ≠ 真實可利用性:

抽象記憶體模型:UAF × PAC

空
指標 p:—

⚠ 此為教學抽象,不等於真實可利用性。UAF 的「形狀」是:釋放後指標沒清(dangling)→ 同一塊被重新配置(重用)→ 透過舊指標操作到新物件。mitigation(PAC/PPL/SPTM-TXM/kalloc_type/zone sequestering)的意義是抬高成本、把攻擊者逼向其他面——不是「不可能」。本模型不含任何 offset/gadget/payload/PoC。

緩解(mitigation):為什麼有效,以及攻擊者被迫轉向哪

這一節是全章的心臟。每個緩解都用同一個框架講:它擋住哪一類能力 → 攻擊者因此被迫做什麼更難的事 → 直到最後只剩 data-only。

KASLR / ASLR:隨機化「東西在哪」

把核心與程式庫的載入位址隨機化。攻擊者原本靠固定位址直接跳到想要的程式碼或資料;隨機化後他先得知道位址。

  • 攻擊者被迫轉向:先找一個資訊洩漏(info leak)把某個基底位址漏出來,才談得上後續。於是「一個記憶體破壞」往往不夠,要先有一個洩漏原語。這抬高了門檻,也讓攻擊鏈變長、更易在某一節被偵測或失敗。

PAC(Pointer Authentication):簽章指標,讓「控制指標」變難

PAC 用一把硬體金鑰在指標未使用的高位塞一個簽章(PAC code),使用前驗章;被竄改的指標驗章失敗 → 程序終止。它主要壓的是「把控制流指標(如返回位址、函式指標)改成攻擊者想要的值」這一手。

  • 攻擊者被迫轉向:既然控制流指標難改,就繞開「改控制流指標」這條路——往改資料走(見下方 data-only)。PAC 不是讓利用變不可能,而是把成本推高、把可走的路收窄。

PPL 與 SPTM / TXM:保護「頁表與簽章狀態」這個元層

即使攻擊者拿到核心讀寫能力,現代 Apple Silicon 還有更高的一層保護「決定什麼程式碼能執行、哪些頁面可寫」的元資料(頁表、code-signing/trust 狀態)。

  • PPL(Page Protection Layer):較早的 Apple Silicon 上,用一個比一般核心碼更高特權的小區域鎖住頁表與簽章相關狀態,使「有了核心讀寫就能改頁表權限/繞 code signing」不再成立。
  • SPTM / TXM(Secure Page Table Monitor / Trusted Execution Monitor):較新的 Apple Silicon 機制,把頁表管理與信任決策從核心進一步切出去,由獨立的更高特權元件管理。
  • 攻擊者被迫轉向:核心讀寫不再自動等於「能改頁面權限/繞簽章/長駐執行任意碼」。要動到那些元狀態,得再攻破這個更高特權層——而它的攻擊面被刻意做得極小。於是攻擊者更傾向不去改頁表/不去執行新碼,改成在現有可寫資料上動手腳(data-only)。

kalloc_type / zone sequestering:讓堆配置「型別隔離」

這三個東西常被混為一談,必須分清(它們是不同時序、不同作用的核心堆強化):

機制它是什麼作用
kalloc.<size>較早的核心堆按大小分桶配置同大小的不同型別物件混在同一桶——這正是 type confusion / UAF 重用的溫床
kalloc_type較新的型別隔離 zone:依呼叫點的型別簽名把配置導向不同 zone不同型別不再共用同一塊重用池,砍掉「釋放 A、用同槽配置 B 再當 A 用」這類重用手法
zone sequestering把虛擬位址空間保留給某 zone,釋放後的位址不立即還給其他用途重用壓制「釋放後該位址被換成不同型別」的時間性重用
  • 攻擊者被迫轉向:經典的「精心安排堆佈局(grooming)讓釋放的物件被換成另一型別」這條路被型別隔離大幅堵死。攻擊者要嘛找同型別內的問題,要嘛——再一次——往 data-only 走。

軍備競賽的終點:data-only

把上面串起來看一個清楚的趨勢:KASLR 要你先洩漏、PAC 擋你改控制指標、PPL/SPTM·TXM 擋你改元狀態、kalloc_type/sequestering 擋你跨型別重用。 每一道都不是「讓利用不可能」,而是把攻擊者從一條路逼到下一條更難的路。

被逼到最後,攻擊者常剩下 data-only:不改控制流、不執行新程式碼、不碰頁表,只在程式本來就會讀寫的資料上動手腳(例如把某個「是否已授權」的旗標、某個物件的權限欄位改成對自己有利的值),讓程式用它自己合法的控制流做出攻擊者要的決策。

近年 CVE 的「根因類別」(不是清單,是模式)

讀漏洞公告時,抓根因類別而不是抓細節。近年 macOS/iOS 修補的記憶體與邏輯問題,反覆落在這幾類模式上:

  • 時間性記憶體破壞(UAF / double free) 仍是核心與使用者態服務的大宗——這正是 kalloc_type/sequestering 要對付的。
  • 型別混淆常出現在序列化/反序列化、跨程序傳遞物件的邊界(呼應 M08 的 XPC 序列化)。
  • TOCTOU / 邏輯漏洞集中在「服務替較低權限的呼叫方做事」的 confused-deputy 場景(呼應 M08/M09)——根因常是責任驗證錯(信了可偽造的身分/路徑)。
  • 整數與越界常在解析不受信任輸入(檔案格式、網路、IPC payload)的程式碼。

🔴 真機操作卡(全部唯讀、安全,macOS 26 適用)

本章是純概念章。下面的指令全部是唯讀觀察,用來親眼印證上面的防禦事實(例如「我這台是 arm64e 還是會跑很多 arm64 app」「這台是哪代 silicon、屬 PPL 還是 SPTM/TXM 世代」),不涉及任何利用、不改任何狀態。需要的話,這些可在你自己的機器上安全執行。

# ── 機型/世代:判斷你這台屬 PPL 還是 SPTM·TXM 世代(唯讀)──
sysctl -n machdep.cpu.brand_string        # CPU 標識(只有 M1 → PPL;M2/M3 及之後 → SPTM·TXM 世代)
sysctl -n hw.optional.arm64               # 是否 arm64(Apple Silicon 為 1)
machine                                   # 架構標識
sw_vers                                   # macOS 版本(緩解隨 OS 演進,事實要鎖版本)

# ── arm64 vs arm64e:印證 PAC 覆蓋率不是「全體」(唯讀)──
otool -hv /usr/sbin/sysctl                # 平台二進位:otool 顯示 cputype=ARM64、caps=USR00(即 arm64e)
file /usr/sbin/sysctl                      # 這裡才會印出字面「arm64e」字樣
otool -hv /Applications/<某第三方App>/Contents/MacOS/<bin>  # 多半是 arm64(caps 0x00,無 user-space PAC)
codesign -dv --verbose=4 /usr/sbin/sysctl 2>&1 | grep -i 'flags\|CDHash'  # 看簽署/runtime 旗標(接 M03)

# ── 位址空間概念印證:看一個自有程序的記憶體佈局(唯讀;接 KASLR/ASLR 直覺)──
vmmap $$ 2>/dev/null | head -40           # 你自己 shell 的記憶體分段(每次啟動位址會變 = ASLR)

下一步:M14 偵測、監控與 DFIR — 把這章「攻擊者被迫轉向哪、鏈在哪幾道邊界跨越」的結構,翻譯成可觀測訊號與偵測規則:當控制流緩解很強、威脅往 data-only 移動時,偵測該從「跑了什麼碼」轉向「狀態/不變式被悄悄改了嗎」。