我們仔細閱讀每一則回饋,並非常重視您的意見。
在 sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 中,函式 svc_rpc_gss_validate() 將 RPC 標頭重建到一個 128 字節的堆疊緩衝區(rpchdr[])中,用於 GSS-API 簽章驗證。它先寫入 32 字節的固定 RPC 標頭欄位,接著將整個 RPCSEC_GSS 認證憑證主體(oa_length 字節)複製到剩餘空間,卻未檢查 oa_length 是否適合。
該緩衝區僅有 128 - 32 = 96 字節可用於憑證主體。任何超過 96 字節的憑證都會導致堆疊緩衝區溢位。
修補程式在複製前新增了邊界檢查。
rpchdr 陣列位於 [rbp-0xc0](比 rbp 低 192 字節),memcpy 寫入位置為 rpchdr + 32 = [rbp-0xa0]。儲存的暫存器與返回地址位於 rpchdr 之上。
然而,憑證主體實際上以 GSS 標頭(版本、程序、序列、服務)及一個 16 字節的上下文句柄開始,導致偏移量向後移動 32 字節,返回地址落在憑證主體第 200 字節。
為何是 NFS?易受攻擊的 kgssapi.ko 模組實作了核心 RPC 子系統的 RPCSEC_GSS 認證。NFS 是主要且通常唯一使用 RPCSEC_GSS 的核心 RPC 服務。NFS 伺服器守護程序(nfsd)監聽 2049/TCP 埠,並在核心上下文處理 RPC 封包,使此漏洞可透過網路遠端執行核心代碼。
為何是 Kerberos?溢位發生在 GSS 驗證程式路徑中。svc_rpc_gss_validate() 僅在建立有效 GSS 上下文後被呼叫。若無有效 GSS 上下文,伺服器會在第三步拒絕封包(回傳 AUTH_REJECTEDCRED),不會觸發易受攻擊的 memcpy。建立有效 GSS 上下文需成功的 Kerberos 握手,攻擊者必須持有 NFS 服務主體的有效 Kerberos 票證。
實際攻擊目標為具備 Kerberos 基礎設施(Active Directory、FreeIPA 等)的企業 NFS 伺服器。任何持有有效 Kerberos 票證的使用者,即使無特權,也能觸發漏洞。測試環境包含自建 KDC,因無預設 Kerberos 環境。
XDR 層對憑證主體強制 MAX_AUTH_BYTES = 400,溢位範圍為 97 至 400 字節(超出安全限制 1 至 304 字節)。
利用過程透過多輪溢位攻擊達成遠端核心代碼執行。每輪利用約可寫入 200 字節 ROP 鏈,432 字節的 shellcode 分 15 輪傳送:第一輪使核心 BSS 可執行,接著 13 輪每輪寫入 32 字節 shellcode,最後一輪寫入剩餘 16 字節並跳轉執行。
每輪利用會透過 kthread_exit() 終止一個 NFS 工作執行緒。FreeBSD 每 CPU 預設產生 8 個 NFS 執行緒,需至少 2 個 CPU(共 16 執行緒)才能完成 15 輪攻擊。單 CPU(8 執行緒)會在第 9 輪失敗,顯示 "GSS context failed"。
shellcode 需放置於可寫且可執行的記憶體區域。核心 BSS 可寫但不可執行(W^X 保護),第一輪利用透過 pmap_change_prot() 新增執行權限,使 BSS 區域變為可讀寫執行。
shellcode 透過多輪利用分段寫入 BSS,最後一輪將 kthread_exit 替換為跳轉 shellcode 入口。
每輪利用需重新建立 GSS 上下文,因前一執行緒已被終止。利用程式使用 Python gssapi 模組,並須使用 kerberos_principal 名稱類型,避免 MIT Kerberos 透過反向 DNS 將主機名稱錯誤正規化,導致票證與伺服器主體不符而被拒絕。
shellcode 在核心模式執行,目標是以 root 權限啟動新程序並執行反向 shell 指令。NFS 工作執行緒為純核心執行緒,無使用者地址空間與 trapframe,無法直接呼叫 execve()。
shellcode 採用兩階段方式:
1. 使用 kproc_create() 建立新程序,該程序擁有完整使用者空間結構與 trapframe,後續可由 kern_execve() 替換為使用者程序。
2. 清除 DR7 硬體除錯暫存器,避免繼承父執行緒的除錯狀態導致除錯例外。
利用中,kproc_create 地址透過預先載入 RBX 寄存器,使用 call rbx 指令呼叫,節省 shellcode 空間。
反向 shell 指令使用 mkfifo 管道方式,因 FreeBSD 內建 nc 不支援 -e 參數,透過管道雙向傳輸命令與輸出。
初始假設 RIP 位於憑證第 168 字節,實際因 GSS 標頭與 XDR 對齊偏移 32 字節,RIP 實際位於第 200 字節,透過遠端送出 De Bruijn 循環模式並分析核心當機紀錄發現。
FreeBSD 的 gssd 守護程序使用 Heimdal GSS-API,攻擊端使用 MIT Kerberos,初期 GSS 上下文建立失敗因 MIT Kerberos 對主機名稱錯誤正規化,透過設定 rdns = false 與 dns_canonicalize_hostname = false 及使用 kerberos_principal 名稱類型解決。
shellcode 正確執行後,工作函式仍因硬體除錯例外(trap 1)崩潰,原因為 kproc_create 繼承父執行緒的除錯暫存器,先前核心恐慌觸發的 DDB 設置硬體斷點未清除。解決方法是在 shellcode 入口清除 DR7。
432 字節 shellcode 無法放入單一 400 字節憑證(扣除 GSS 標頭、填充、暫存器與 ROP 鏈後),早期嘗試使用 rep movsq 批次複製失敗,因可用 gadget 有副作用且產生垃圾資料。
最終採用 pop rdi + pop rax + mov [rdi], rax 實作,每 8 字節寫入需 40 字節 ROP 鏈,無副作用且無垃圾資料,雖較慢但穩定。
每輪溢位終止一個 NFS 工作執行緒,需 15 輪完成。GSS 上下文建立亦佔用執行緒。FreeBSD 預設每 CPU 8 執行緒,單 CPU 約第 9 輪失敗,需至少 2 CPU。實務中多 CPU 伺服器執行緒數較多,非限制因素。
總封包數 15 個 RPCSEC_GSS 溢位封包,總耗時約 45 秒,成功取得互動式 root 權限反向 shell。