文章目录
-
- 一、先把"容器不是虚拟机"说透
-
- [1.1 虚拟机 vs 容器](#1.1 虚拟机 vs 容器)
- [1.2 Docker 栈里你实际在防谁](#1.2 Docker 栈里你实际在防谁)
- 二、隔离机制:逃逸就是逐个击破它们
-
- [2.1 Namespaces(命名空间)](#2.1 Namespaces(命名空间))
- [2.2 Cgroups(资源限制)](#2.2 Cgroups(资源限制))
- [2.3 Capabilities(能力)](#2.3 Capabilities(能力))
- [2.4 Seccomp / AppArmor / SELinux](#2.4 Seccomp / AppArmor / SELinux)
- [2.5 设备与挂载](#2.5 设备与挂载)
- 三、逃逸路径全景:先分清"配错了"和"真漏洞"
-
- [3.1 配置型(实战中更多)](#3.1 配置型(实战中更多))
- [3.2 漏洞型(经典 CVE 主战场)](#3.2 漏洞型(经典 CVE 主战场))
- [3.3 "逃逸成功"的判定(授权测试与应急)](#3.3 “逃逸成功”的判定(授权测试与应急))
- [四、五个经典 CVE(原理 · 影响 · 防护)](#四、五个经典 CVE(原理 · 影响 · 防护))
-
- [CVE-1:CVE-2019-5736 ------ runc 容器逃逸("覆盖宿主机 runc")](#CVE-1:CVE-2019-5736 —— runc 容器逃逸(“覆盖宿主机 runc”))
- [CVE-2:CVE-2022-0492 ------ cgroup v1 `release_agent` 容器逃逸/提权](#CVE-2:CVE-2022-0492 —— cgroup v1
release_agent容器逃逸/提权) - [CVE-3:CVE-2020-15257 ------ containerd-shim 抽象 Unix 套接字暴露](#CVE-3:CVE-2020-15257 —— containerd-shim 抽象 Unix 套接字暴露)
- [CVE-4:CVE-2019-14271 ------ `docker cp` 与恶意库加载](#CVE-4:CVE-2019-14271 ——
docker cp与恶意库加载) - [CVE-5:CVE-2024-21626 ------ runc "Leaky Vessels" 文件描述符泄漏](#CVE-5:CVE-2024-21626 —— runc “Leaky Vessels” 文件描述符泄漏)
- [五个 CVE 对照表](#五个 CVE 对照表)
- [五、比 CVE 更常见:三条"免 0day"逃逸链](#五、比 CVE 更常见:三条“免 0day”逃逸链)
-
- [5.1 挂载 docker.sock](#5.1 挂载 docker.sock)
- [5.2 --privileged 或 cap-add=ALL](#5.2 --privileged 或 cap-add=ALL)
- [5.3 挂载宿主机 / 或关键目录](#5.3 挂载宿主机 / 或关键目录)
- 六、防御体系:从镜像到运行时
-
- [6.1 构建与供应链](#6.1 构建与供应链)
- [6.2 部署策略](#6.2 部署策略)
- [6.3 节点与运行时](#6.3 节点与运行时)
- [6.4 检测与响应](#6.4 检测与响应)
- [6.5 入门检查命令(自查,非攻击)](#6.5 入门检查命令(自查,非攻击))
- 七、授权测试如何评估"逃逸风险"
- 八、结语
-
- [附录 A:术语表](#附录 A:术语表)
- [附录 B:配置型风险优先级](#附录 B:配置型风险优先级)
写在前面
容器让应用打包、交付和扩缩变得轻量,却也带来一种危险错觉:
"跑在容器里,就等于和宿主机隔离了;就算被打穿应用,攻击者也出不去。"
对红队和真实入侵来说,容器往往只是权限与文件系统的薄包装 。一旦配置失误,或底层 runc / containerd / 内核存在漏洞,攻击者就可能从"容器内普通用户/root"走到宿主机 root,进而接管节点、集群,乃至云账号。
这类行为通常被叫作 容器逃逸(Container Escape)。
本文是《网络安全实战》容器方向的入门篇,重点讲清三件事:
- Docker/容器隔离到底靠什么,薄在哪里;
- 逃逸常见路径(配置型 vs 漏洞型);
- 五个影响深远的经典 CVE:原理级解读、影响面、检测与修复------不提供可直接武器化的利用代码。
面向安全从业者、运维、DevSecOps 与授权测试人员。所有验证请在自有靶场或书面授权环境进行。
一、先把"容器不是虚拟机"说透
1.1 虚拟机 vs 容器
| 维度 | 虚拟机 | 容器(如 Docker) |
|---|---|---|
| 隔离核心 | 硬件虚拟化 + 客户机内核 | 共享宿主机内核 + 命名空间等 |
| 启动成本 | 较重 | 很轻 |
| 攻击面 | Hypervisor、设备模拟 | 内核、运行时、编排、错误挂载 |
| 逃逸含义 | 打穿 Hypervisor/管理面 | 突破命名空间/cgroup/能力限制,控宿主机 |
容器与宿主机共享同一内核。因此:
- 内核漏洞可能同时影响所有容器与宿主机;
- 容器内
root并不自动等于"无害",它仍可能有危险能力(capabilities); - "逃逸"常常是:在共享内核的前提下,拿到宿主机视角的进程、文件系统或管理套接字。
1.2 Docker 栈里你实际在防谁
一次典型调用链(简化):
text
docker CLI / API
→ dockerd
→ containerd
→ runc(创建容器进程)
→ 应用进程(在 namespaces 里)
安全边界分布在:
- 镜像与供应链(有没有后门、有没有高危包)
- 引擎 API(docker.sock 谁能碰)
- 运行时(runc/containerd 漏洞)
- 内核(namespace/cgroup/文件系统)
- 编排层(Kubernetes 的 Pod 安全、RBAC、宿主机 Path 挂载)
入门阶段先抓住 Docker 单机逃逸,再延伸到 K8s。
二、隔离机制:逃逸就是逐个击破它们
2.1 Namespaces(命名空间)
常见命名空间:
| Namespace | 隔离内容 |
|---|---|
| PID | 进程视图 |
| Mount | 文件系统挂载 |
| Network | 网卡、协议栈、端口 |
| UTS | 主机名 |
| IPC | 进程间通信 |
| User | 用户 ID 映射(user namespace) |
| Cgroup | cgroup 视图(较新) |
逃逸常表现为:在宿主机 mount/PID 命名空间里执行代码,或通过错误挂载直接改宿主机文件。
2.2 Cgroups(资源限制)
限制 CPU、内存、PID 数量等,主要防资源耗尽,不是强安全边界 。
历史上 cgroup 相关漏洞(如 release_agent 问题)说明:资源子系统配置不当或内核缺陷,也能变成提权/逃逸踏板。
2.3 Capabilities(能力)
容器内即使是 root,也被拆成细粒度能力。危险能力示例:
CAP_SYS_ADMIN:接近"万能钥匙",大量挂载/命名空间操作CAP_SYS_PTRACE:调试/注入其它进程CAP_NET_ADMIN、CAP_DAC_READ_SEARCH、CAP_SYS_MODULE等
错误示范 :--privileged 几乎把能力、设备和 cgroup 限制一并放开,等于邀请逃逸。
2.4 Seccomp / AppArmor / SELinux
- Seccomp:限制系统调用集合
- AppArmor/SELinux:强制访问控制,限制进程能碰的文件与操作
默认配置能挡一批套路;关闭或自定义过宽会显著降低逃逸成本。
2.5 设备与挂载
宿主机路径挂入容器,是最常见的配置型逃逸:
text
-v /:/host
-v /var/run/docker.sock:/var/run/docker.sock
--device /dev/sda
攻击者不必"打破魔法隔离",只要在容器里操作被挂进来的宿主机根目录或 Docker 套接字,就能等价控宿主机。
三、逃逸路径全景:先分清"配错了"和"真漏洞"
3.1 配置型(实战中更多)
- Privileged 容器
- 挂载 docker.sock
- 挂载宿主机敏感路径 (
/、/etc、/var/run、Kubernetes 的hostPath) - 危险 capability 过多
- PID/Network host 模式滥用
- 可写的宿主机卷 + 计划任务/服务目录
- Docker API 对公网暴露且无认证
这类问题用基线扫描、准入策略(OPA/Kyverno)、镜像与部署审核就能大幅消灭。
3.2 漏洞型(经典 CVE 主战场)
- runc 实现缺陷(创建/执行阶段文件描述符、覆盖宿主机二进制)
- containerd 暴露内部 API
- docker cp 等引擎操作中的路径/链接竞争
- 内核 cgroup/命名空间/文件系统漏洞
- 与容器特性交互的提权(在容器内提权后再借配置出逃)
下文五个 CVE,覆盖运行时、引擎操作、内核 cgroup、shim 接口等代表性类别。
3.3 "逃逸成功"的判定(授权测试与应急)
通常至少满足之一:
- 在宿主机 PID/Mount 命名空间执行任意命令;
- 改写宿主机关键文件(
/etc/shadow、定时任务、ssh 密钥、容器运行时二进制); - 通过 docker.sock 创建特权容器并落地宿主机控制;
- 获得宿主机 root 等价会话。
应急时不要只看容器内进程,要查:宿主机是否出现异常进程、异常导出、异常 docker 事件、运行时二进制是否被改。
四、五个经典 CVE(原理 · 影响 · 防护)
版本号、补丁包请以各发行版安全公告与发行说明为准;下文强调机制与防御,不给利用步骤。
CVE-1:CVE-2019-5736 ------ runc 容器逃逸("覆盖宿主机 runc")
时间背景:2019 年披露,震动整个容器生态。
问题出在哪
runc 负责创建容器。漏洞条件下,恶意容器可在特定执行路径中,借助对 /proc/self/exe 等机制的操作,最终写到宿主机上的 runc 二进制 。之后宿主机再执行 docker exec / 创建容器等操作时,会跑到被篡改的 runc,从而实现宿主机代码执行。
为何经典
- 不依赖
--privileged也能在特定条件下构成致命链; - 攻击对象是共享运行时二进制这一架构共性;
- 影响 Docker、Kubernetes、containerd、CRI-O 等一切依赖脆弱 runc 的栈。
防御与检测要点
- 升级 runc/Docker/containerd 到修复版本(首选)。
- 镜像供应链:禁止不明镜像;只读根文件系统;禁止容器内乱装。
- 完整性监控:监视宿主机
runc二进制哈希突变。 - 运行时策略:减少对不可信镜像执行
docker exec;用签名与准入。 - 审计:异常容器在短时间内触发大量 exec、异常写
/proc相关行为(需运行时安全产品辅助)。
启示
共享运行时 = 共享命运。运行时补丁优先级应等同于关键内核补丁。
CVE-2:CVE-2022-0492 ------ cgroup v1 release_agent 容器逃逸/提权
时间背景:2022 年,Linux 内核 cgroup 相关。
问题出在哪
在 cgroup v1 中,release_agent 可在 cgroup 变空时由内核执行用户指定路径。权限校验存在问题的情况下,容器内进程可能借此布置并触发以更高权限在宿主机上下文执行的逻辑,从而逃逸或提权。
利用条件(概念)
通常与下列因素有关:
- 内核版本是否受影响;
- 容器是否具备足够能力(常与
CAP_SYS_ADMIN、cgroup 命名空间/挂载情况相关); - 是否仍暴露可写的 cgroup 文件系统路径。
因此它常与"特权容器 / 过多能力 / 错误挂载"叠加出现。
防御与检测要点
- 升级内核到修复版本。
- 能上 cgroup v2 的环境优先迁移(减少 v1 攻击面)。
- 禁止特权容器 ;去掉不必要的
CAP_SYS_ADMIN。 - 运行时检测:容器内异常挂载 cgroupfs、写
release_agent、异常notify_on_release。 - K8s:Pod Security Standards / 策略引擎拒绝 privileged 与危险 capabilities。
启示
资源控制子系统也是安全边界的一部分;"不是网络协议,就不会被打"是错觉。
CVE-3:CVE-2020-15257 ------ containerd-shim 抽象 Unix 套接字暴露
时间背景:2020 年,containerd 生态。
问题出在哪
containerd-shim 等组件使用了抽象命名空间 Unix 套接字(不落盘路径的一种 AF_UNIX 形态)。在网络命名空间配置不当的场景下,容器内攻击者可能访问到宿主机上的 shim API,进而影响容器生命周期管理,造成逃逸级后果。
为何值得单独记住
- 展示了"命名空间没配好时,管理面套接字会漏进容器";
- 与 docker.sock 挂载类似,同属管理 API 暴露家族,只是路径更隐蔽。
防御与检测要点
- 升级 containerd 到修复版本。
- 关注容器网络模式:
host网络会显著放大此类风险。 - 检测:容器内扫描抽象套接字、异常访问 shim API 的行为(依赖运行时安全/审计)。
- 原则:任何管理套接字都不应进入不可信工作负载命名空间。
启示
逃逸不一定改文件,能调用宿主机旁的管理 API 同样算逃逸。
CVE-4:CVE-2019-14271 ------ docker cp 与恶意库加载
时间背景 :2019 年,Docker Engine 的 docker cp 路径。
问题出在哪
从容器拷贝文件到宿主机时,引擎侧处理过程在特定条件下可能加载到容器文件系统中的动态库 (与 NSS 等解析路径有关)。若攻击者在容器内放置恶意库,可在宿主机执行 docker cp 时于宿主机进程上下文加载,从而实现逃逸。
攻击叙事(概念)
- 攻击者已控制容器文件系统内容;
- 诱导或等待管理员/自动化对容器执行
docker cp; - 宿主机上的 Docker 相关进程加载恶意库并执行。
防御与检测要点
- 升级 Docker Engine 至修复版本。
- 运维上:对不可信容器避免随意
docker cp;用只读、最小权限、经安全扫描的镜像。 - 监控:对不可信容器频繁
docker cp的审计事件告警。 - 供应链:防止构建阶段植入恶意
libnss_*.so之类文件。
启示
宿主机上的运维操作也可能成为逃逸触发器;工具命令不是"天生安全"。
CVE-5:CVE-2024-21626 ------ runc "Leaky Vessels" 文件描述符泄漏
时间背景:2024 年初披露,同属 runc 高危家族,常被称作 Leaky Vessels 相关问题之一。
问题出在哪
runc 在处理某些容器过程时,内部文件描述符泄漏,使容器进程可能获得对宿主机文件系统敏感路径的访问能力(例如通过泄漏的目录 fd 进行后续操作)。配合镜像构建或运行时的特定工作目录等条件,可导致逃逸。
为何选作"经典"
- 证明 2019 年之后,运行时 fd 处理仍是高价值攻击面;
- 影响面同样覆盖依赖 runc 的容器平台;
- 提醒:逃逸研究已高度关注"创建阶段的边角实现"。
防御与检测要点
- 立即升级 runc 及发行版提供的 Docker/containerd 相关包。
- 镜像构建环境与 CI runner 必须同步打补丁(构建逃逸可打穿 CI 宿主机)。
- 运行时安全:检测异常的宿主机路径访问、异常 WORKINGDIR 行为(产品能力相关)。
- 不可信工作负载与构建任务隔离到独立节点池。
启示
补丁运营要覆盖生产节点 + 构建节点 + 开发机 Docker Desktop/引擎,不能只打生产。
五个 CVE 对照表
| CVE | 组件 | 类型关键词 | 优先动作 |
|---|---|---|---|
| 2019-5736 | runc | 覆盖宿主机运行时 | 升级 runc;监控二进制完整性 |
| 2022-0492 | Kernel cgroup | release_agent | 升级内核;禁 privileged;迁 cgroup v2 |
| 2020-15257 | containerd | 管理套接字暴露 | 升级;慎用 host 网络 |
| 2019-14271 | Docker Engine | docker cp 加载恶意库 | 升级引擎;慎对不可信容器 cp |
| 2024-21626 | runc | fd 泄漏致宿主机访问 | 升级 runc;隔离构建池 |
五、比 CVE 更常见:三条"免 0day"逃逸链
入门读者必须掌握------真实渗透里它们比追新 CVE 更管用。
5.1 挂载 docker.sock
容器内能访问 docker.sock,即可对 dockerd 发 API:拉镜像、起特权容器、挂载宿主机根目录。
等价于宿主机 root 管理权。
缓解:默认不挂;需用时用 rootless、细粒度代理(如带策略的 socket proxy)、或彻底改架构。
5.2 --privileged 或 cap-add=ALL
特权容器可看到更多设备、可挂载、可操作 cgroup,大量公开逃逸技术变得"按说明操作即可"。
缓解:K8s 准入拒绝;Docker 编排模板扫描;仅排障临时开,开完关掉。
5.3 挂载宿主机 / 或关键目录
-v /:/host 后,容器内改 /host/etc/cron.d、/host/root/.ssh 即可。
缓解:只挂业务需要的子目录;只读挂载;禁止 hostPath 到根与命名空间目录。
六、防御体系:从镜像到运行时
6.1 构建与供应链
- 最小基础镜像;多阶段构建;
- 镜像签名(Cosign)+ 准入只跑签名镜像;
- SCA 扫 CVE;禁止不明公共镜像直接进生产。
6.2 部署策略
- 非 root 用户跑进程(
USER指令 +runAsNonRoot); - 只读根文件系统;
- drop 全部 capabilities,再按需加回;
- 禁止 privileged、hostPID、hostNetwork(默认);
- seccomp/AppArmor 默认配置保留。
6.3 节点与运行时
- 及时升级 containerd/runc/内核;
- docker API 仅本地 unix socket,不暴露 2375;
- 宿主机加固:SSH 收敛、审计、完整性监控。
6.4 检测与响应
- 运行时安全(Falco、商业 CWPP 等):特权启动、写 docker.sock、写
/proc/release_agent、容器内挂载宿主机盘; - 审计
docker events、K8s audit; - 发现逃逸:隔离节点、驱逐负载、轮换密钥与凭证、重装节点(高信任场景)。
6.5 入门检查命令(自查,非攻击)
bash
# 容器能力与特权相关信息(示例)
docker inspect <id> --format '{{.HostConfig.Privileged}}'
docker inspect <id> --format '{{.HostConfig.CapAdd}} {{.HostConfig.CapDrop}}'
docker inspect <id> --format '{{json .Mounts}}'
# 宿主机运行时版本
docker version
runc --version
containerd --version
uname -r
把输出与安全基线对照:Privileged=true、挂载了 docker.sock 或 /,即高危。
七、授权测试如何评估"逃逸风险"
- 先做配置审计,再考虑漏洞利用;报告里配置问题往往占多数。
- 标明目标运行时与内核版本,对照公告是否在受影响范围。
- 演示逃逸必须在隔离靶场;生产仅做版本与配置验证。
- 报告写清:前提条件(是否 privileged、挂载、能力) 、补丁版本 、检测规则建议。
- 禁止留下改写过的宿主机 runc/后门服务。
八、结语
容器安全入门的核心认知可以压成三句话:
- 容器共享内核,隔离是策略叠加,不是魔法墙。
- 生产里最常见的"逃逸"是 docker.sock、privileged 和危险挂载,不必等待 0day。
- 经典 CVE(runc 覆盖、cgroup release_agent、shim 套接字、docker cp、fd 泄漏)证明:运行时与内核补丁必须进入强制运营。
从 2019 到 2024,逃逸手法从"覆盖 runc"演进到"泄漏 fd",但防守主线未变:
最小权限运行 + 管理面零暴露 + 运行时与内核持续更新 + 运行时行为检测。
建议你读完本文后立刻做三件事:
- 盘点环境中所有
Privileged=true与挂载docker.sock的容器,能下线立即下线; - 核对节点
runc/containerd/内核版本是否覆盖上述五类修复; - 给 CI 构建节点单独打补丁并与生产同等安全等级。
把这三步做完,你就已经超过只靠"容器里别用 root"口头禅的多数团队------而那正是容器安全从入门到可运营的分界线。
附录 A:术语表
| 术语 | 含义 |
|---|---|
| 逃逸 | 从容器约束突破到宿主机控制力 |
| runc | OCI 运行时参考实现之一 |
| containerd | 容器管理守护进程 |
| capability | Linux 细粒度特权 |
| cgroup | 资源限制与记账子系统 |
| docker.sock | Docker 守护进程 API 套接字 |
附录 B:配置型风险优先级
| 优先级 | 项 |
|---|---|
| P0 | 公网 Docker API、宿主机根目录挂载、生产 privileged |
| P1 | docker.sock 挂载、CAP_SYS_ADMIN、hostPID/hostNetwork |
| P2 | 可写敏感 hostPath、未签名镜像、root 跑服务 |