- MinIO简介
MinIO是美国MinIO公司的一款开源的对象存储服务器, 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。MinIO中存在一处信息泄露漏洞,由于Minio集群进行信息交换的9000端口,在未经配置的情况下通过发送特殊HPPT请求进行未授权访问,进而导致MinIO对象存储的相关环境变量泄露,环境变量中包含密钥信息。泄露的信息中包含登录账号密码。
2.漏洞描述 MinIO是美国MinIO公司的一款开源的对象存储服务器。该产品支持构建用于机器学习、分析和应用程序数据工作负载的基础架构。 MinIO 存在信息泄露漏洞,该漏洞源于在集群部署中MinIO会返回所有环境变量,导致信息泄露。
CVE-2023-28432 CNNVD-202303-1795
3.影响版本 2019-12-17T23-16-33Z <= MinIO < RELEASE.2023-03-20T20-16-18Z
4.fofa查询语句 banner=“MinIO” || header=“MinIO” || title=“MinIO Browser”
5.漏洞复现 漏洞链接:http://xxx.com/minio/bootstrap/v1/verify 注意数据包使用post的方式
漏洞分析
我先访问到main.go,主函数代码主要指向“minio "github.com/minio/minio/cmd"”
说明Minio启动之后大部分逻辑都会在cmd这个目录中,可能会包含路由规则。
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/main.go
package main // import "github.com/minio/minio"
import (
"os"
// MUST be first import.
_ "github.com/minio/minio/internal/init"
minio "github.com/minio/minio/cmd"
)
func main() {
minio.Main(os.Args)
}
接下来,我看到routers.go的第75行包含路由注册,通过信息泄漏的载荷之中“bootstrap”字段结合路由注册代码,进行跟踪。
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/routers.go line 75
func configureServerHandler(endpointServerPools EndpointServerPools) (http.Handler, error) {
// Initialize router. `SkipClean(true)` stops minio/mux from
// normalizing URL path minio/minio#3256
router := mux.NewRouter().SkipClean(true).UseEncodedPath()
// Initialize distributed NS lock.
if globalIsDistErasure {
registerDistErasureRouters(router, endpointServerPools)
}
// Add Admin router, all APIs are enabled in server mode.
registerAdminRouter(router, true)
// Add healthcheck router
registerHealthCheckRouter(router)
// Add server metrics router
registerMetricsRouter(router)
// Add STS router always.
registerSTSRouter(router)
// Add KMS router
registerKMSRouter(router)
// Add API router
registerAPIRouter(router)
router.Use(globalHandlers...)
return router, nil
}
紧接着,我找到bootstrap-peer-server.go,这个文件作为后续的代码逻辑。
https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/bootstrap-peer-server.go line 38
const (
bootstrapRESTVersion = "v1"
bootstrapRESTVersionPrefix = SlashSeparator + bootstrapRESTVersion
bootstrapRESTPrefix = minioReservedBucketPath + "/bootstrap"
bootstrapRESTPath = bootstrapRESTPrefix + bootstrapRESTVersionPrefix
)
在新逻辑中首先找到接收HTTP请求的函数,分别在第130行与第132行。通过代码逻辑来看,getServerSystemCfg应该作为接受参数之后
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/bootstrap-peer-server.go line 130
// HealthHandler returns success if request is valid
func (b *bootstrapRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) {}
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/bootstrap-peer-server.go line 132
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))
}
继续查看getServerSystemCfg做了什么?主要获取的环境变量skipEnvs[envK]。
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/bootstrap-peer-server.go line 110
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,
}
}
skipEnvs[envK]是什么呢?skipEnvs[envK]包含环境变量信息。根据官方说明,MinIO在启动时会从环境变量中读取预先设置的用户和密码,默认情况下:minioadmin/minioadmin。
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/bootstrap-peer-server.go line 103
var skipEnvs = map[string]struct{}{
"MINIO_OPTS": {},
"MINIO_CERT_PASSWD": {},
"MINIO_SERVER_DEBUG": {},
"MINIO_DSYNC_TRACE": {},
}
我简单搜索了下MinIO项目,预先设置密码的情况还是比较严重。例如decom.sh等。
# https://github.com/minio/minio/blob/fb1492f5317bddb263092b69218c5a2c23025d19/docs/distributed/decom.sh line 23
export MC_HOST_myminio="http://minioadmin:minioadmin@localhost:9000/"
./mc admin user add myminio/ minio123 minio123
./mc admin user add myminio/ minio12345 minio12345
./mc admin policy create myminio/ rw ./docs/distributed/rw.json
./mc admin policy create myminio/ lake ./docs/distributed/rw.json
./mc admin policy attach myminio/ rw --user=minio123
./mc admin policy attach myminio/ lake,rw --user=minio12345
当通过信息泄漏获得账号密码之后,可以登陆MinIO更新恶意升级URL,并且执行update触发RCE。
MinIO对于更新包进行了sha256sum,但是由于envMinisignPubKey为空,所以sha256sum失效了。
# https://github.com/minio/minio/blob/RELEASE.2023-03-13T19-46-17Z/cmd/update.go line 541
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
}
我反向跟踪 verifyBinary方法,找到Update的HTTP入口,其中就包含了调用sha256sum校验。
# https://github.com/minio/minio/blob/6017b63a0612daa0ab9ef87804a57b899766bb9c/cmd/admin-handlers.go line 77
// ServerUpdateHandler - POST /minio/admin/v3/update?updateURL={updateURL}
// ----------
// updates all minio servers and restarts them gracefully.
# https://github.com/minio/minio/blob/6017b63a0612daa0ab9ef87804a57b899766bb9c/cmd/admin-handlers.go line 157
for _, nerr := range globalNotificationSys.VerifyBinary(ctx, u, sha256Sum, releaseInfo, reader) {
if nerr.Err != nil {
err := AdminError{
Code: AdminUpdateApplyFailure,
Message: nerr.Err.Error(),
StatusCode: http.StatusInternalServerError,
}
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
logger.LogIf(ctx, fmt.Errorf("server update failed with %w", err))
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
}
}
err = verifyBinary(u, sha256Sum, releaseInfo, mode, reader)