不可執行 mfd 簡介

作者:

Daniel Verkamp <dverkamp@chromium.org> Jeff Xu <jeffxu@chromium.org>

貢獻者:

Aleksa Sarai <cyphar@cyphar.com>

自從 Linux 引入 memfd 功能以來,memfd 始終設定了其執行位,並且 memfd_create() 系統呼叫不允許以不同的方式設定它。

然而,在預設安全的系統中,例如 ChromeOS(其中所有可執行檔案都應來自受驗證啟動保護的 rootfs),memfd 的這種可執行性質為 NoExec 繞過打開了一扇門,並啟用了“confused deputy attack”(混淆代理攻擊)。 例如,在 VRP 漏洞 [1] 中:cros_vm 程序建立了一個 memfd 以與外部程序共享內容,但是 memfd 被覆蓋並用於執行任意程式碼和 root 提權。[2] 列出了更多此類 VRP。

另一方面,可執行 memfd 有其合法的用途:runc 使用 memfd 的 seal 和可執行功能來複制二進位制檔案的內容,然後執行它們。 對於這樣的系統,我們需要一種解決方案來區分 runc 對可執行 memfd 的使用和攻擊者的使用 [3]。

為了解決以上問題
  • 允許 memfd_create() 在建立時設定 X 位。

  • 當 NX 被設定時,允許 memfd 被密封以修改 X 位。

  • 新增一個新的 pid 名稱空間 sysctl:vm.memfd_noexec,以幫助應用程式遷移和強制執行不可執行的 MFD。

使用者 API

int memfd_create(const char *name, unsigned int flags)

MFD_NOEXEC_SEAL

flags 中設定了 MFD_NOEXEC_SEAL 位時,memfd 使用 NX 建立。 設定 F_SEAL_EXEC,並且 memfd 無法被修改以稍後新增 X。 MFD_ALLOW_SEALING 也被暗示。 這是應用程式使用 memfd 的最常見情況。

MFD_EXEC

flags 中設定了 MFD_EXEC 位時,memfd 使用 X 建立。

注意

MFD_NOEXEC_SEAL 意味著 MFD_ALLOW_SEALING。 如果應用程式不希望密封,則可以在建立後新增 F_SEAL_SEAL。

Sysctl:

pid 名稱空間 sysctl vm.memfd_noexec

新的 pid 名稱空間 sysctl vm.memfd_noexec 有 3 個值

  • 0:MEMFD_NOEXEC_SCOPE_EXEC

    沒有 MFD_EXEC 或 MFD_NOEXEC_SEAL 的 memfd_create() 行為就像設定了 MFD_EXEC 一樣。

  • 1:MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL

    沒有 MFD_EXEC 或 MFD_NOEXEC_SEAL 的 memfd_create() 行為就像設定了 MFD_NOEXEC_SEAL 一樣。

  • 2:MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED

    沒有 MFD_NOEXEC_SEAL 的 memfd_create() 將被拒絕。

該 sysctl 允許對未設定可執行位的舊軟體進行更精細的 memfd_create 控制; 例如,vm.memfd_noexec=1 的容器意味著舊軟體將預設建立不可執行的 memfd,而新軟體可以透過設定 MFD_EXEC 來建立可執行的 memfd。

vm.memfd_noexec 的值在建立時傳遞給子名稱空間。 此外,該設定是分層的,即在 memfd_create 期間,我們將從當前 ns 搜尋到 root ns,並使用最嚴格的設定。

[1] https://crbug.com/1305267

[2] https://bugs.chromium.org/p/chromium/issues/list?q=type%3Dbug-security%20memfd%20escalation&can=1

[3] https://lwn.net/Articles/781013/