CVE-2023-28434: Insufficient Information (NVD-CWE-noinfo)
value: Minio is a Multi-Cloud Object Storage framework. Prior to RELEASE.2023-03-20T20-16-18Z, an attacker can use crafted requests to bypass metadata bucket name checking and put an object into any bucket while processing `PostPolicyBucket`. To carry out this attack, the attacker requires credentials with `arn:aws:s3:::*` permission, as well as enabled Console API access. This issue has been patched in RELEASE.2023-03-20T20-16-18Z. As a workaround, enable browser API access and turn off `MINIO_BROWSER=off`. 

CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 8.8
Published: 2023-03-22T21:15Z
Repository: https://github.com/minio/minio
Language: Go
Files changed: cmd/api-router.go, cmd/auth-handler.go
Commit date: 2023-03-19 21:15:20 -0700
Commit message: fix: post policy request security bypass (#16849)


Diff: diff --git a/cmd/api-router.go b/cmd/api-router.go
index 95360945c9..e09b7bc254 100644
--- a/cmd/api-router.go
+++ b/cmd/api-router.go
@@ -432,12 +432,13 @@ func registerAPIRouter(router *mux.Router) {
 			collectAPIStats("putbucket", maxClients(gz(httpTraceAll(api.PutBucketHandler)))))
 		// HeadBucket
 		router.Methods(http.MethodHead).HandlerFunc(
 			collectAPIStats("headbucket", maxClients(gz(httpTraceAll(api.HeadBucketHandler)))))
 		// PostPolicy
-		router.Methods(http.MethodPost).HeadersRegexp(xhttp.ContentType, "multipart/form-data*").HandlerFunc(
-			collectAPIStats("postpolicybucket", maxClients(gz(httpTraceHdrs(api.PostPolicyBucketHandler)))))
+		router.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool {
+			return isRequestPostPolicySignatureV4(r)
+		}).HandlerFunc(collectAPIStats("postpolicybucket", maxClients(gz(httpTraceHdrs(api.PostPolicyBucketHandler)))))
 		// DeleteMultipleObjects
 		router.Methods(http.MethodPost).HandlerFunc(
 			collectAPIStats("deletemultipleobjects", maxClients(gz(httpTraceAll(api.DeleteMultipleObjectsHandler))))).Queries("delete", "")
 		// DeleteBucketPolicy
 		router.Methods(http.MethodDelete).HandlerFunc(
diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go
index addf5a7b79..947b8ee9d7 100644
--- a/cmd/auth-handler.go
+++ b/cmd/auth-handler.go
@@ -23,10 +23,11 @@ import (
 	"crypto/subtle"
 	"encoding/base64"
 	"encoding/hex"
 	"errors"
 	"io"
+	"mime"
 	"net/http"
 	"net/url"
 	"strconv"
 	"strings"
 	"sync/atomic"
@@ -72,12 +73,15 @@ func isRequestPresignedSignatureV2(r *http.Request) bool {
 	return ok
 }
 
 // Verify if request has AWS Post policy Signature Version '4'.
 func isRequestPostPolicySignatureV4(r *http.Request) bool {
-	return strings.Contains(r.Header.Get(xhttp.ContentType), "multipart/form-data") &&
-		r.Method == http.MethodPost
+	mediaType, _, err := mime.ParseMediaType(r.Header.Get(xhttp.ContentType))
+	if err != nil {
+		return false
+	}
+	return mediaType == "multipart/form-data" && r.Method == http.MethodPost
 }
 
 // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation.
 func isRequestSignStreamingV4(r *http.Request) bool {
 	return r.Header.Get(xhttp.AmzContentSha256) == streamingContentSHA256 &&

Vulnerable code: 		router.Methods(http.MethodPost).HeadersRegexp(xhttp.ContentType, "multipart/form-data*").HandlerFunc(
			collectAPIStats("postpolicybucket", maxClients(gz(httpTraceHdrs(api.PostPolicyBucketHandler)))))
	return strings.Contains(r.Header.Get(xhttp.ContentType), "multipart/form-data") &&
		r.Method == http.MethodPost
Fixed code: 		router.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool {
			return isRequestPostPolicySignatureV4(r)
		}).HandlerFunc(collectAPIStats("postpolicybucket", maxClients(gz(httpTraceHdrs(api.PostPolicyBucketHandler)))))
	"mime"
	mediaType, _, err := mime.ParseMediaType(r.Header.Get(xhttp.ContentType))
	if err != nil {
		return false
	}
	return mediaType == "multipart/form-data" && r.Method == http.MethodPost