首頁 資訊 runC 逃逸漏洞 CVE

runC 逃逸漏洞 CVE

來源:泰然健康網(wǎng) 時間:2025年09月20日 04:08

2024 年 1 月 31 日,容器運行時組件 runc 發(fā)布了 1.1.12 版本,修復(fù)了容器逃逸漏洞 CVE-2024-21626。受影響的版本范圍為:

大于等于 v1.0.0-rc93,小于等于 1.1.11。

已修復(fù)版本為 1.1.12。

對于第三方組件:

containerd 的已修復(fù)版本為 1.6.28 和 1.7.13。受影響版本范圍為 1.4.71.6.271.7.12。docker 的已修復(fù)版本為 25.0.2。

復(fù)現(xiàn)環(huán)境:

Linux 發(fā)行版:Arch LinuxLinux 內(nèi)核版本:6.4.12-arch1-1Docker 版本:24.0.6runC 版本:1.1.9

根據(jù)該漏洞的形成原理,攻擊者可通過以下兩種方式利用該漏洞:

啟動一個容器時將容器的工作目錄設(shè)置為 /proc/self/fd/<fd>(<fd> 為打開 /sys/fs/cgroup 目錄時返回的文件描述符,一般情況下為 7 或 8)。

在容器中為 /proc/self/fd/<fd>(<fd> 為打開 /sys/fs/cgroup 目錄時返回的文件描述符,一般情況下為 7 或 8) 創(chuàng)建一個符號鏈接。當(dāng)外部在該容器中執(zhí)行命令時,容器中可通過 /proc/<PID>/cwd 符號鏈接訪問宿主機文件系統(tǒng)中的 /sys/fs/cgroup 目錄,并利用形如 /proc/<PID>/cwd/../../../ 的路徑訪問宿主機文件系統(tǒng)。

根據(jù)漏洞原理編寫 Dockerfile 并構(gòu)建出惡意鏡像,直接運行鏡像即可成功利用。

/images/cve-2024-21626-escape-via-crafted-image.gif

先啟動一個容器,并在容器中為 /proc/self/fd/8 創(chuàng)建符號鏈接 /foo。此處使用 debian:bookworm 鏡像。

接著在新的窗口中使用 docker exec 命令在新創(chuàng)建的容器中執(zhí)行 sleep 命令,并設(shè)置工作目錄為 /foo。

在容器中找到 sleep 命令的 PID,然后通過其 /proc/<PID>/cwd 即可訪問到宿主機文件系統(tǒng)。

/images/cve-2024-21626-escape-via-exec.gif

首先簡要描述當(dāng)執(zhí)行 docker run 命令創(chuàng)建并運行一個容器時,幾個組件之間的整體調(diào)用關(guān)系是怎樣的;然后通過 runc run 命令復(fù)現(xiàn)漏洞,并說明漏洞的形成原理;接著解釋為什么通過 docker run 命令創(chuàng)建的容器中 /sys/fs/cgroup 目錄對應(yīng)的文件描述符為 8,而不是 7。最后對官方修復(fù)代碼進行分析。

當(dāng)執(zhí)行 docker run 命令創(chuàng)建并運行一個容器時,幾個組件之間的調(diào)用關(guān)系為:

/run/containerd/containerd.sock

fork(2) & execve(2)

fork(2) & execve(2)

dockerd

containerd

containerd-shim-runc-v2

runc

Docker 引擎(dockerd)通過 /run/containerd.containerd.sock 調(diào)用 containerd 的 RPC 服務(wù)創(chuàng)建并運行容器。containerd 接收到創(chuàng)建和運行容器的請求后,會先執(zhí)行 containerd-shim-runc-v2 命令運行一個 RPC 服務(wù)。containerd-shim-runc-v2 進程的 RPC 服務(wù)以 UNIX socket 文件的形式對外提供,UNIX socket 文件的路徑保存在 /run/containerd/io.containerd.v2.task/moby/<containerID>/address 文件中。RPC 服務(wù)的定義位于 /api/runtime/task/v3/shim.proto。containerd 使用這個 UNIX socket 文件與之交互。當(dāng) containerd 調(diào)用 containerd-shim-runc-v2 的 Create 方法創(chuàng)建一個容器時,containerd-shim-runc-v2 會執(zhí)行 runc create 命令創(chuàng)建容器。當(dāng) containerd 調(diào)用 Run 方法運行一個容器時,containerd-shim-runc-v2 會執(zhí)行 runc start 命令運行指定的容器。

另外,containerd 把對 runc 命令的調(diào)用封裝成了一個單獨的庫 go-runc。

使用 alpine 鏡像運行一個容器,并將其導(dǎo)出,作為 rootfs。

使用 runc 命令創(chuàng)建一個默認(rèn)的配置文件 config.json,并將其中 cwd 鍵的值修改為 /proc/self/fd/7。

使用 runc 命令啟動容器,實現(xiàn)逃逸。需要注意的是**必須添加 --log 參數(shù),原因稍后解釋。

~/container/runc/runc --version docker run --name helper-ctr alpine docker export helper-ctr --output alpine.tar mkdir rootfs tar xf alpine.tar -C rootfs ~/container/runc/runc spec sed -ri 's#(s*"cwd": )"(/)"#1 "/proc/self/fd/7"#g' config.json grep cwd config.json sudo ~/container/runc/runc --log ./log.json run demo

runc run 命令在剛啟動時會創(chuàng)建一個 libcontainer.linuxContainer 對象。在創(chuàng)建此對象之前會先創(chuàng)建一個用于操作 cgroup 的接口類型對象 cgroups.Manager,由于 runc 操作 cgroup 的實現(xiàn)原理,它會打開宿主機文件系統(tǒng)中的 /sys/fs/cgroup 目錄,后續(xù)對 cgroup 文件的打開操作都是基于 openat2(2) 系統(tǒng)調(diào)用。但是在生成子進程時并沒有把 /sys/fs/cgroup 目錄的文件描述符關(guān)閉,以至于子進程仍可以利用該文件描述符的 /proc/self/fd/<fdnum> 符號鏈接訪問宿主機文件系統(tǒng)。如果 openat(2) 調(diào)用失敗,那么會調(diào)用 openFallback() 函數(shù)以絕對路徑的方式打開 cgroup 文件。

相關(guān)函數(shù)調(diào)用鏈為:

if openat2 syscall failed

startContainer

createContainer

(*linuxFactory).Create

(*manager).GetFreezerState

cgroups.prepareOpenat2

openFallback

runc 在 2020 年 12 月 4 日的代碼中引入了 openat(2) 的支持,即 1.0.0-rc93 版本。簡言之,使用 openat(2) 可以避免在容器的 mount 命名空間中掛載宿主機文件系統(tǒng)的目錄時潛在的逃逸風(fēng)險,詳細(xì)問題不在此處展開,可以查看相關(guān)文章以及 openat(2) 的文檔。

這個問題與 Golang 的運行時有關(guān)。首先 0、1、2 三個文件描述符毫無疑問代表標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤;打開命令行參數(shù)指定的日志文件(3);Golang 程序剛啟動時運行時會創(chuàng)建一個 epoll 文件描述符(4)和一個管道(5、6);接著初始化 cgroup 管理模塊時打開 /sys/fs/cgroup 目錄(7)。

打開日志文件在前,Golang 運行時創(chuàng)建 epoll 文件描述符和管道在后的原因與 Golang 運行時的實現(xiàn)有關(guān),由于篇幅限制不展開,后續(xù)會新開一篇文章講解。

從前述調(diào)用鏈可知,最終執(zhí)行 runc 命令的是 containerd-shim-runc-v2,而 containerd-shim-runc-v2 在執(zhí)行 runc 命令前會創(chuàng)建一個 UNIX socket 提供 RPC 服務(wù),而這個 UNIX socket 對應(yīng)的文件描述符會被傳遞給 runc 命令。通過修改源碼,在 C 函數(shù) nsexec() 開頭添加 sleep() 函數(shù),可以觀察到 containerd-shim-runc-v2 和 runc create 兩個進程的文件描述符關(guān)系。

/images/rpc-socket-passed-to-runc.png

由于在 nsexec() 函數(shù)中添加的 sleep 函數(shù),runc create 進程進入 main 函數(shù)后立即進入睡眠,此時從上圖可以看到其有 4 個文件描述符:

0:不需要給 runc create 輸入任何數(shù)據(jù),因此標(biāo)準(zhǔn)輸入被重定向到 /dev/null。1、2:containerd-shim-runc-v2 需要捕獲 runc 進程的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤,因此把 1 和 2 兩個文件描述符設(shè)置為管道。3:文件描述符 3 就是 containerd-shim-runc-v2 誤傳遞給 runc create 進程的提供 RPC 服務(wù)的 UNIX socket。

/images/sys-fs-cgroup-with-fd-8.png

上圖是 runc create 進程通過執(zhí)行 /proc/self/exe init 命令,且 /proc/self/exe init 進程阻塞到我們添加的 sleep() 函數(shù)時 runc create 進程的文件描述符情況??梢钥吹接捎谟糜谔峁?RPC 服務(wù)的 UNIX socket 文件描述符 3 的存在,/sys/fs/cgroup 目錄的文件描述符變成了 8。

為什么有時候用 docker run 創(chuàng)建的容器中 /sys/fs/cgroup 目錄的文件描述符仍然是 7?這個問題目前還沒有完全搞清楚,不過猜測還是與前述提到的 exec.(*Cmd).ExtraFiles 的實現(xiàn)機制有關(guān)。

在前面使用 runc 復(fù)現(xiàn)漏洞時,如果不指定 --log 參數(shù)的話 /sys/fs/cgroup 目錄的文件描述符會變成 3,此時是無法傳遞給子進程的,原因是與標(biāo)準(zhǔn)庫 os/exec 中 exec.(*Cmd).ExtraFiles 的實現(xiàn)機制有關(guān)。

眾所周知,在 Linux 中創(chuàng)建一個子進程需要執(zhí)行 fork(2) 系統(tǒng)調(diào)用,子進程會繼承父進程打開的所有文件描述符。在子進程中執(zhí)行 execve(2) 系統(tǒng)調(diào)用加載一個新的程序時,內(nèi)核會關(guān)閉所有設(shè)置了 O_CLOEXEC 標(biāo)志的文件描述符,而子進程新加載的程序依然可以通過其余沒有關(guān)閉的文件描述符訪問對應(yīng)的文件。這種情況下就會存在一定的安全風(fēng)險,例如 CVE-2024-21626,runC 主進程沒有及時關(guān)閉 /sys/fs/cgroup 目錄的文件描述符,導(dǎo)致容器進程可通過繼承自主進程的文件描述符訪問宿主機文件系統(tǒng)。

面對這個安全風(fēng)險,Golang 的設(shè)計原則是,默認(rèn)情況下子進程不應(yīng)該繼承父進程的所有文件描述符,如果子進程確要繼承某些文件描述符,父進程需要顯式地傳遞給子進程。具體來說,Golang 運行時在打開文件時會給文件描述符設(shè)置 O_CLOEXEC 標(biāo)志,對于要傳遞給子進程的文件描述符,運行時會使用 dup3(2) 系統(tǒng)調(diào)用以去掉 O_CLOEXEC 標(biāo)志,從而實現(xiàn)描述符的傳遞。注意,直接調(diào)用系統(tǒng)調(diào)用創(chuàng)建的文件描述符(例如使用 syscall 標(biāo)準(zhǔn)庫或者 golang.org/x/sys/unix 庫)默認(rèn)是不會被設(shè)置 O_CLOEXEC 標(biāo)志的,如果不顯式傳遞給子進程,其最終能否傳遞給子進程取決于上述提到的運行時使用 dup3(2) 復(fù)制文件描述符時的具體情況,即如果文件描述符的值大于 len(cmd.ExtraFiles) + 3,那么它就不會被關(guān)閉,否則會被關(guān)閉。詳情請閱讀 Golang 源碼。

查看倉庫發(fā)現(xiàn)為了修復(fù)此漏洞,共提交了 4 次代碼 8e1cd2、f2f162、89c93d、ee7309,其中后三次都是在產(chǎn)生子進程前關(guān)閉不需要的文件描述符或者給文件描述符設(shè)置 O_CLOEXEC 標(biāo)志位,第一次提交的代碼對當(dāng)前工作目錄作校驗,看其是否屬于容器中的目錄。

根據(jù)漏洞利用過程,可以總結(jié)出漏洞利用過程中幾條進程行為特征:

在容器中會產(chǎn)生當(dāng)前工作目錄(cwd)形如 /proc/self/fd/<fd> 的進程。在容器中會產(chǎn)生目標(biāo)目錄為形如 /proc/self/fd/<fd> 的 symlink(2) 或 symlinkat(2) 系統(tǒng)調(diào)用。在容器中會產(chǎn)生 open/openat/openat2 系統(tǒng)調(diào)用,且文件名具有 /proc/d+/cwd/.* 的正則表達式特征。

synk 官方提供了一個基于 eBPF 的檢測程序 leaky-vessels-dynamic-detector。

根據(jù)上述漏洞利用特征編寫如下 Falco 規(guī)則。

- macro: container condition: (container.id != host and container.name exists) - rule: CVE-2024-21626 (runC escape via /proc/[PID]/cwd) exploited desc: > Detect CVE-2024-21626, runC escape vulnerability via /proc/[PID]/cwd. condition: > container and ((evt.type = execve and proc.cwd startswith /proc/self/fd) or (evt.type in (open, openat, openat2) and fd.name glob "/proc/*/cwd/*") or (evt.type in (symlink, symlinkat) and fs.path.target startswith "/proc/self/fd/")) and proc.name != "runc:[1:CHILD]" output: CVE-2024-21626 exploited (%container.info) priority: CRITICAL

經(jīng)測試發(fā)現(xiàn)并不會產(chǎn)生任何告警。分析之后發(fā)現(xiàn),對于 open 系列系統(tǒng)調(diào)用,F(xiàn)alco 是使用容器的 rootfs 對符號鏈接實現(xiàn)解引用,也就是說 /proc/39/cwd/../../../etc/passwd 在 Falco 中實際拿到的數(shù)據(jù)為 /etc/passwd。以下是訪問宿主機文件系統(tǒng)時容器中所有的 open 系列系統(tǒng)調(diào)用。

17:57:28.598911006: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /proc) 17:57:33.931961621: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 <NA>) 17:57:33.931983557: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /etc/ld.so.cache) 17:57:33.932034308: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 <NA>) 17:57:33.932060422: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /lib/x86_64-linux-gnu/libpcre2-8.so.0) 17:57:33.932202422: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 <NA>) 17:57:33.932224321: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /lib/x86_64-linux-gnu/libc.so.6) 17:57:33.933222540: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 <NA>) 17:57:33.933269859: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /proc/self/maps) 17:57:33.933623118: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 <NA>) 17:57:33.933664988: Critical CVE-2024-21626 exploited (container=ad2561294d80 container_id=ad2561294d80 container_name=cve-2024-21626 /etc/passwd) 18:12:02.847468476: Critical CVE-2024-21626 exploited (container=96925ad4d71a container_id=96925ad4d71a container_name=cve-2024-21626) 18:12:02.848096708: Critical CVE-2024-21626 exploited (container=96925ad4d71a container_id=96925ad4d71a container_name=cve-2024-21626)

https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv

https://github.com/opencontainers/runc/commit/8e1cd2f56d518f8d6292b8bb39f0d0932e4b6c2a

https://github.com/opencontainers/runc/commit/f2f16213e174fb63e931fe0546bbbad1d9bbed6f

https://github.com/opencontainers/runc/commit/89c93ddf289437d5c8558b37047c54af6a0edb48

https://github.com/opencontainers/runc/commit/ee73091a8d28692fa4868bac81aa40a0b05f9780

https://access.redhat.com/security/cve/cve-2024-21626

https://github.com/snyk/leaky-vessels-dynamic-detector

https://snyk.io/blog/cve-2024-21626-runc-process-cwd-container-breakout/

https://nvd.nist.gov/vuln/detail/CVE-2024-21626

https://github.com/snyk/leaky-vessels-dynamic-detector

https://man7.org/linux/man-pages/man2/openat2.2.html

https://lkml.kernel.org/linux-fsdevel/20191026185700.10708-1-cyphar@cyphar.com/

相關(guān)知識

安全知識,等保,密評,網(wǎng)絡(luò)安全
Windows核彈漏洞披露!CVE
【漏洞通告】Apache Log4j2 遠(yuǎn)程代碼執(zhí)行漏洞(CVE
CVE
2024漏洞風(fēng)險啟示錄:在攻防的螺旋中尋找「治愈」之道
IngressNightmare:Ingress Nginx 再曝5個安全漏洞,可接管你的 K8s 集群
研究人員披露GE HealthCare超聲波醫(yī)療設(shè)備漏洞,能被用于部署惡意程序
官方通報ComfyUI存多個高危漏洞:已被境外黑客利用對我國網(wǎng)絡(luò)實施攻擊
研究人員揭露GE HealthCare超音波醫(yī)療設(shè)備漏洞,並指出能被用於部署惡意程式、搜刮病人檢查結(jié)果
云安全日報201026:Linux內(nèi)核發(fā)現(xiàn)數(shù)據(jù)泄露和特權(quán)升級漏洞,需要盡快升級

網(wǎng)址: runC 逃逸漏洞 CVE http://www.u1s5d6.cn/newsview1816856.html

推薦資訊