1. 執行摘要

報告對 CVE-2026-20971 進行完整的技術剖析,這是一個高嚴重性的 use-after-free (UAF) 漏洞,存在於 Samsung 專有的 KNOX 安全子系統 PROCA 與 FIVE 中。該缺陷存在於 Kernel procfs handlers 中,這些 handlers 會與 process 完整性追蹤物件互動,影響橫跨 8 年發佈的 Samsung Android 硬體,包含 Galaxy S9 至 S25、所有 A 系列裝置,以及 Exynos 與 Qualcomm 晶片組 [1] 。該漏洞允許無特權的使用者空間應用程式,透過一個狹窄的搶佔式 race window 觸發 Kernel 記憶體損壞,產生三種不同的 exploit 基礎操作:記憶體資訊洩漏、受控制的 spinlock 寫入,以及有條件的 indirect function call。現代的 Android Kernel Control Flow Integrity (KCFI) 消除了任意執行的 primitive,而 slab cache 隔離與嚴格的排程限制,儘管 CVSS 7.8 的高嚴重性評分,仍造成顯著的攻擊障礙。Samsung 已在 2026 年 1 月的 Android Security Monthly Release (SMR) 修補套件中完全修復此漏洞。

當KNOX防禦成弱點!UAF如何擊穿PROCA與FIVE? | 資訊安全新聞

2. 背景:Samsung PROCA 與 FIVE 完整性子系統

Samsung 的 Process Authenticator (PROCA) 是一個整合至 KNOX 的封閉原始碼 Kernel 安全模組,目的在透過 runtime process 驗證來阻止未經授權的二進位執行。FIVE 扮演 PROCA 的 Kernel 端完整性追蹤層,擴展 Linux 原生的 IMA (Integrity Measurement Architecture),以維護每個執行中 task 的信任狀態 [1] 。每個 Linux task_struct 都繫結到一個 refcounted task_integrity 物件,該物件從專用的 slab cache ( task_integrity_cache ) 分配。此物件儲存關鍵的信任 metadata,包括完整性等級旗標、file 標籤,以及觸發安全重設的可執行檔案參照。

為了將完整性狀態暴露給診斷工具,Samsung 在 /proc/[pid]/integrity/ 下增加了 Custom procfs entry。使用者空間對這些節點的讀取請求會觸發 Kernel handlers,這些 handlers 直接 dereference task 的 integrity 指標,而無需增加物件的 reference counter——這是導致 UAF race condition 的根本結構性缺陷 [1]

3. 根本原因分析:Race Condition 邏輯缺陷

3.1 Kernel 有漏洞的巨集與物件替換流程

在所有三個 procfs handlers 中使用的關鍵不安全指標擷取巨集定義如下:

  1. // Raw pointer fetch without refcount increment (unsafe core macro)
  2. #define TASK_INTEGRITY(task) ((task)->integrity)

當一個 process 呼叫 execve() 來載入新的可執行檔時, Kernel 會在 __five_bprm_check() 中使用以下序列替換附加的 task_integrity 物件:

  1. // Integrity object replacement logic inside execve handler
  2. old_tint = TASK_INTEGRITY(task);
  3. tint = task_integrity_alloc(); // Allocate new integrity tracker
  4. task_integrity_assign(task, tint); // Attach new object to task
  5. task_integrity_put(old_tint); // Decrement refcount and free old object

記憶體安全失效的發生,是因為 Kernel 在 釋放 舊物件 之前 就分配並附加了新的 integrity 物件。在 Android 完全搶佔式 Kernel 上,執行可以在任何指令邊界被暫停。這創造了一個狹窄的 race window,其中 procfs 讀取執行緒會擷取到過時的 old_tint 指標,然後被排程出去,並在原始物件已被釋放後恢復執行 [1]

3.2 Race Condition 執行序列圖

sequenceDiagram participant APP_ATTACKER as Attacker App Thread (procfs reader) participant TARGET_CHILD as Target Child Process (execve caller) participant KERNEL as Linux Preemptive Kernel Scheduler APP_ATTACKER->KERNEL: Open /proc/$CHILD_PID/integrity/value KERNEL->APP_ATTACKER: Execute proc_integrity_value_read() Note over APP_ATTACKER,KERNEL: Fetch stale tint pointer via TASK_INTEGRITY() KERNEL-->>APP_ATTACKER: Preempt thread, suspend execution mid-handler TARGET_CHILD->KERNEL: Call execve("/system/bin/monkey") KERNEL->KERNEL: __five_bprm_check() allocates new task_integrity KERNEL->KERNEL: task_integrity_put(old_tint) frees stale object KERNEL-->>APP_ATTACKER: Resume suspended procfs read handler APP_ATTACKER->KERNEL: task_integrity_user_read(stale freed pointer) Note over APP_ATTACKER,KERNEL: UAF memory access triggered

圖 1:序列圖視覺化觸發 use-after-free 記憶體存取缺陷的最小 race window,邏輯源自原始漏洞揭露 [1]

4. 有漏洞的 Procfs Handler 程式碼分析(5 個 Kernel 程式碼片段)

此節分析三個暴露的 procfs read handlers。

4.1 proc_integrity_value_read() (資訊洩漏)

  1. // Vulnerable procfs handler for integrity value reading
  2. static int proc_integrity_value_read(struct seq_file *m, struct task_struct *task)
  3. {
  4. // Fetch raw pointer without refcount hold
  5. seq_printf(m, "%x\n", task_integrity_user_read(TASK_INTEGRITY(task)));
  6. return 0;
  7. }
  8. // Direct memory read from tint->user_value at offset 0
  9. static inline enum task_integrity_value task_integrity_user_read(struct task_integrity *intg)
  10. {
  11. return intg->user_value;
  12. }

此 handler 提供了一個穩定的資訊洩漏基本操作,且無立即的 Kernel 當機風險。如果被釋放的 task_integrity slab slot 被重新分配給攻擊者控制的記憶體,handler 會將回收記憶體區域的前 64 位元組字組列印到使用者空間。這個資訊洩漏機制(oracle)允許攻擊者驗證 slab 重複使用是否成功,並且在搭配次要記憶體損壞基本功能時可以繞過 KASLR [1] 。主要的限制是需要在指標提取和記憶體讀取之間,命中搶佔中斷點所需的極狹窄排程時間窗。

4.2 proc_integrity_reset_file() (有條件的 Indirect Function Call)

  1. // Vulnerable reset_file path handler with uncontrolled pointer dereference
  2. static int proc_integrity_reset_file(struct seq_file *m, struct task_struct *task)
  3. {
  4. char *tmp;
  5. char *pathname;
  6. // Dereference stale tint->reset_file pointer without validation
  7. if (!TASK_INTEGRITY(task)->reset_file) {
  8. seq_printf(m, "%s", "");
  9. return 0;
  10. }
  11. tmp = (char *)__get_free_page(GFP_KERNEL);
  12. // Critical uncontrolled chained pointer dereference chain
  13. pathname = d_path(&TASK_INTEGRITY(task)->reset_file->f_path, tmp, PAGE_SIZE);
  14. return 0;
  15. }

d_path() 函式內部會 dereference 巢狀 Kernel 結構( struct file struct path struct dentry dentry_operations )來解析檔案系統路徑。如果被釋放的 reset_file 指標被攻擊者控制的記憶體覆寫,執行流程會流向 d_op->d_dname() 的 indirect call。研究指出了一個確定性的方法,可透過執行 /system/bin/monkey (一個純文字、非可執行的系統二進位,會使 ELF/script 解析失敗)來將 reset_file refcount 精確設為 1,從而觸發 FIVE 的 reset file 指派邏輯 [1]

4.2.1 KCFI 緩解措施

所有現代 Samsung Kernel 都透過以下建構配置旗標啟用 Clang KCFI:

  1. # Kernel Makefile configuration enabling Control Flow Integrity
  2. ifdef CONFIG_CFI_CLANG
  3. CC_FLAGS_CFI := -fsanitize=kcfi
  4. KBUILD_CFLAGS += $(CC_FLAGS_CFI)
  5. endif

KCFI 在每個 indirect function call 之前插入執行時期 prototype 匹配檢查。 d_dname 呼叫站點強制執行嚴格的型別簽章驗證,幾乎消除了此 primitive 所有可行的任意 Kernel 程式碼執行目標。任何無效的函式指標跳轉都會透過 CFI 違規錯誤觸發立即的 Kernel panic [1] ,使得此 exploit 路徑在現實世界的權限提升中實際上不可行。

4.3 proc_integrity_label_read() (受控制的 Spinlock 寫入基礎功能)

  1. // Vulnerable label read handler with unprotected spinlock operation on stale pointer
  2. static int proc_integrity_label_read(struct seq_file *m, struct task_struct *task)
  3. {
  4. struct integrity_label *l;
  5. // Capture stale tint pointer before spinlock acquisition
  6. spin_lock(&TASK_INTEGRITY(task)->value_lock);
  7. l = TASK_INTEGRITY(task)->label;
  8. spin_unlock(&TASK_INTEGRITY(task)->value_lock);
  9. if (l) {
  10. size_t data_len = l->len * 2;
  11. bin2hex(buffer, l->data, size / 2);
  12. seq_commit(m, size);
  13. }
  14. return 0;
  15. }

value_lock spinlock 成員位於 task_integrity 結構的 offset 0xc 處。當過時指標的 slab slot 被重新分配時, spin_lock() 會在固定偏移處對回收的記憶體執行 atomic 32 位元寫入操作。這會建立一個受約束的任意寫入基礎功能,能夠損壞重疊 slab 物件中的 refcounts、長度欄位或指標值。攻擊者也可以利用 spinlock 爭用,透過強制對過時鎖定位址的 CPU 爭用,來人為地擴大 race window,不過長時間的 spin 迴圈會在 10-20 秒後觸發 hard lockup watchdog panic [1]

5. 攻擊障礙與緩解措施有效性分析

5.1 固有的 Exploit 限制

  1. 專用 Slab Cache 隔離: task_integrity 物件是從一個獨特的專用 cache 分配的,需要跨 slab memory spray 技術才能將攻擊者控制的物件放入被釋放的 slot 中——這顯著增加了 exploit 的複雜性。
  2. 微秒級的 Race Window: 關鍵的搶佔中斷點僅存在於少數 CPU opcodes 之間,需要精確的 Kernel 排程操作才能可靠地觸發 UAF 條件。
  3. KCFI 執行阻斷: 如 4.2.1 節所述,Control Flow Integrity 完全中和了 indirect function call primitive,消除了最強大的程式碼執行向量。

5.2 降低風險的原生 Android Kernel 防禦

除了 KCFI 之外,標準的 Android 強化層(包括 KASLR、slab 隨機化和記憶體標記)進一步提高了攻擊門檻。雖然該漏洞的 CVSS 分數仍為 7.8(高),因為理論上可能造成完全的裝置接管影響,但在現實世界中,武器化需要串聯多階段的記憶體損壞基礎操作,且根據 LucidBit Labs 的測試,失敗率極高 [1]

6. 修復與修補程式概述

Samsung 在 2026 年 1 月的 SMR 安全性更新中修復了 CVE-2026-20971。 Kernel 修復程式修改了所有三個有漏洞的 procfs handlers,在透過 task_integrity_get() 提取指標後立即增加 task_integrity refcount,並在 handler 退出前搭配對應的 task_integrity_put() 。此 reference counting 確保在 procfs 讀取操作進行期間,integrity 物件不會被釋放,從而完全消除 UAF race window [1] 。所有受影響的 Galaxy S、A 系列及摺疊裝置都在 2026 年 1 月中旬收到了修補程式更新。

7. 研究結論:防禦機制作為新的攻擊面

CVE-2026-20971 說明了一個關鍵的安全設計模式:廠商專有的安全擴充功能(此例中為 KNOX/FIVE)引入了未記錄、複雜的 Kernel 邏輯,從而擴大了整體攻擊面。該漏洞之所以持續存在於 8 年的裝置發行中,是因為三個複合性的設計疏忽:針對 procfs 暴露的 Kernel 物件缺少 reference counting、物件替換邏輯中存在緊密的搶佔 race window,以及在使用者空間可存取的 handlers 中未考慮專用 slab cache 物件的生命週期。這項研究證明,即使存在主要的記憶體損壞缺陷,諸如 KCFI 等現代 Kernel 緩解措施在阻止關鍵的 exploit 基礎操作方面非常有效。未來安全的 Kernel 開發應強制對所有使用者空間可觸及的 Kernel 物件指標進行 refcount 驗證,並對自訂 procfs 或 sysfs 子系統中未受保護的 slab 物件 dereference 進行靜態分析掃描