惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MyScale Blog
MyScale Blog
Jina AI
Jina AI
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
The Cloudflare Blog
T
Threat Research - Cisco Blogs
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
AI
AI
Scott Helme
Scott Helme
Attack and Defense Labs
Attack and Defense Labs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
LangChain Blog
Recent Announcements
Recent Announcements
Security Latest
Security Latest
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Last Week in AI
Last Week in AI
Security Archives - TechRepublic
Security Archives - TechRepublic
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
S
Securelist
S
Security Affairs
Project Zero
Project Zero
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
T
Tor Project blog
A
About on SuperTechFans
V2EX - 技术
V2EX - 技术
宝玉的分享
宝玉的分享
T
Tenable Blog
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
Simon Willison's Weblog
Simon Willison's Weblog
Forbes - Security
Forbes - Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
V2EX
AWS News Blog
AWS News Blog
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
阮一峰的网络日志
阮一峰的网络日志
I
InfoQ
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
美团技术团队

空鸣深语

CVE-2026-23086浅析 《Arcaea》双星纪念 KDE Plasma自动设置时区 OCI运行时:容器创建流程 CVE-2024-21626浅析 CVE-2022-0847浅析 寓言:糊房子 有了工位之后:重新运转的日常 「瞬间」交织「记忆」:横滨站观后与圣地巡礼 我的音游心路历程 修正TUN下NetworkManager的网络状态 从「分歧路口」到「前途漫漫」:上海站观后 《少女乐队的呐喊》短评 恢复小爱课程表的「教务导入」 北航软件工程小记:用Rust开发「风行旅途」 写作即取舍:我的文字与版面 读书:《进化心理学:心理的新科学》 root仍在路上,现实却愈发收紧 浅谈选择困难症——以点外卖为例 必应索引失而复得小记 告别常驻进程:如何让微信使用FCM推送 《你想活出怎样的人生》影评——在留白中遨游 窥探镜中的自己:阿斯伯格综合征的诊断轨迹 首次踏入live house:躁动氛围与孤独心声 远程设置引发的Firefox Android搜索引擎缺失问题排查与恢复
CVE-2019-5736浅析
深鸣 · 2026-04-04 · via 空鸣深语

背景

容器架构

Docker等容器采用分层架构,大致可以分为三层:

  1. dockerd(API层):处理高层逻辑。负责镜像管理、网络分配及存储卷挂载。
  2. containerd(管理层):容器生命周期守护进程。负责拉取镜像、利用OverlayFS准备根文件系统(rootfs),并调用底层的运行时。
  3. runc(执行层):OCI标准运行时。它是宿主机上的一个轻量级二进制文件(runc,对于Docker而言为docker-runc),通过clonepivot_root等系统调用配置Namespace、Cgroups等,创建隔离环境,并最终在隔离区内执行目标命令。每当执行docker exec时,宿主机会启动一个新的runc实例进入已有的隔离空间。

(关于更多内容,参见《Journey From Containerization To Orchestration And Beyond》)

ETXTBSY

根据POSIX标准(IEEE Std 1003.1):

[ETXTBSY]The file is a pure procedure (shared text) file that is being executed and oflag is O_WRONLY or O_RDWR.

如果二进制文件正在被进程执行,那么无法以写入模式(O_WRONLYO_RDWR)打开并修改它。 内核会直接抛出ETXTBSY错误。

runc是沟通宿主机和容器的桥梁。当其进入容器命名空间执行指令时,runc本身会暴露在容器的视角中。但在正常逻辑中,内核的ETXTBSY机制会禁止任何进程修改正在运行的二进制镜像,这能够防止容器内进程对宿主机实体的反向篡改。

攻击流程

简而言之,旧版本的runc在容器内重新执行自身时,意外暴露了宿主机可执行文件在物理层面的写入路径。Dragon Sector指出了该漏洞的攻击流程:

  1. 攻击者在容器内,把某个常用命令(如/bin/sh)替换成符号链接,直接指向/proc/self/exe
  2. 宿主机的管理员执行docker exec -it <Container ID> /bin/sh。宿主机上的/usr/bin/runc进程启动,配置好Namespace等隔离机制后,进入容器内部。
  3. runc执行容器中的/bin/sh,顺着软链接执行/proc/self/exe,即重新执行了自己一次。
  4. 此时runc已处于容器的文件系统中,因此会加载容器内部相应的动态链接库。而攻击者已将容器里相关的链接库替换,其带有恶意全局构造函数,会在runcmain函数之前运行。
  5. 恶意代码先以只读模式(O_RDONLY)打开/proc/self/exe,拿到其文件描述符后,启动另一个独立的恶意脚本,并向之传递文件描述符。
  6. 当原本的runc进程退出后,独立运行的恶意脚本可以以写入模式(O_WRONLY)重新打开它,并将之覆写为恶意程序。只要宿主机再调用runc,恶意代码就会在宿主机执行。

他们还提到,也可以不采用替换动态链接库的方式。当runc调用execve系统调用,去执行#!/proc/self/exe时,其dumpable标志位被重置为1。这意味着其他进程可以读取该进程的信息。因此,攻击者可以在容器中执行恶意程序,判断/proc/<runc PID>/exe是否可访问;当其可访问时,可将之打开,拿到指向宿主机/usr/bin/runc的文件描述符,随后不断尝试写入。下面的复现流程即采用该方法。

复现

该仓库提供了漏洞的PoC。其首先覆写了/bin/sh文件,然后不断查找runc对应的PID,最终尝试将恶意代码写入该文件。考虑到演示效果,若攻破宿主机,则会在/tmp中创建tomorin_was_here文件,并写入内容MyGO!!!!!。为避免链接库版本差异而带来的问题,我采用静态编译(即传入CGO_ENABLED=0)。

为了避免污染宿主机,我使用Vagrant搭建虚拟机环境,并采用Libvirt作为后端。虚拟机中使用Metarget构建环境。对于该漏洞,使用Ubuntu 18.04可以搭建相关环境。

首先创建后台运行的普通容器,名为victim:

1
$ sudo docker run -d --name victim ubuntu:18.04 sleep infinity

随后将生成的二进制文件复制到该容器中,并执行:

1
2
3
4
$ sudo docker cp /home/vagrant/shared_code/CVE-2019-5736/poc/exploit victim:/exploit
$ sudo docker exec -it victim /bin/bash
# chmod +x /exploit
# ./exploit

创建被污染的容器

该容器中的/bin/sh即被替换成了指向/proc/self/exe的软链接,该容器已被污染。

当管理员尝试打开该容器时:

1
$ sudo docker exec -it victim /bin/sh

可以看到容器中的恶意程序旋即打开了宿主机上的/usr/bin/docker-runc,并将其覆写为恶意程序。同时containerd可能是为了清理残留状态,再次调用了runc,恶意代码得以执行。因此可以观察到/tmp/tomorin_was_here文件被创建,并写入了MyGO!!!!!

1
2
3
4
$ ls -l /tmp/tomorin_was_here
-rw-r--r-- 1 root root 10 Apr 4 04:55 /tmp/tomorin_was_here
$ cat /tmp/tomorin_was_here
MyGO!!!!!

访问容器后宿主机被攻破

修复与后续

runc该commit中修复了该漏洞。其核心是通过memfd_create系统调用切断容器到物理文件的连接。

修复后的runc在进入容器前,会在内存中创建独立的匿名文件,将自身的二进制代码拷贝至此,并执行密封操作。随后,程序转为执行该内存副本。即使容器内进程通过/proc/self/exe等方式获取到fd,也无法触及宿主机的/usr/bin/runc物理文件。此外,由于内存中的可执行文件被密封,因此也不会遭到修改。

LXC开发者Christian Brauner在其博文中批评了包括Docker在内的特权容器,称这种容器内部的root(UID 0)等同于宿主机的root,因此是「不安全的组件」,无法实现所谓的「root安全性」(即即便容器里的用户获得了root权限,也无法对宿主机安全构成威胁)。因此,他极力倡导将非特权容器作为默认标准。