VYPR
High severityNVD Advisory· Published Mar 22, 2023· Updated Feb 25, 2025

Minio Privilege Escalation on Windows via Path separator manipulation

CVE-2023-28433

Description

Minio is a Multi-Cloud Object Storage framework. All users on Windows prior to version RELEASE.2023-03-20T20-16-18Z are impacted. MinIO fails to filter the \ character, which allows for arbitrary object placement across buckets. As a result, a user with low privileges, such as an access key, service account, or STS credential, which only has permission to PutObject in a specific bucket, can create an admin user. This issue is patched in RELEASE.2023-03-20T20-16-18Z. There are no known workarounds.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/minio/minioGo
< 0.0.0-2023032007350.0.0-202303200735

Affected products

1
  • Range: < RELEASE.2023-03-20T20-16-18Z

Patches

2
b3c54ec81e0a

reject object names with '\' on windows (#16856)

https://github.com/minio/minioHarshavardhanaMar 20, 2023via ghsa
3 files changed · +72 6
  • cmd/object-api-multipart_test.go+14 1 modified
    @@ -22,6 +22,7 @@ import (
     	"context"
     	"fmt"
     	"reflect"
    +	"runtime"
     	"strings"
     	"testing"
     
    @@ -32,6 +33,9 @@ import (
     
     // Wrapper for calling NewMultipartUpload tests for both Erasure multiple disks and single node setup.
     func TestObjectNewMultipartUpload(t *testing.T) {
    +	if runtime.GOOS == globalWindowsOSName {
    +		t.Skip()
    +	}
     	ExecObjectLayerTest(t, testObjectNewMultipartUpload)
     }
     
    @@ -110,9 +114,18 @@ func testObjectAbortMultipartUpload(obj ObjectLayer, instanceType string, t Test
     		{"--", object, uploadID, BucketNotFound{}},
     		{"foo", object, uploadID, BucketNotFound{}},
     		{bucket, object, "foo-foo", InvalidUploadID{}},
    -		{bucket, "\\", uploadID, InvalidUploadID{}},
     		{bucket, object, uploadID, nil},
     	}
    +
    +	if runtime.GOOS != globalWindowsOSName {
    +		abortTestCases = append(abortTestCases, struct {
    +			bucketName      string
    +			objName         string
    +			uploadID        string
    +			expectedErrType error
    +		}{bucket, "\\", uploadID, InvalidUploadID{}})
    +	}
    +
     	// Iterating over creatPartCases to generate multipart chunks.
     	for i, testCase := range abortTestCases {
     		err = obj.AbortMultipartUpload(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, opts)
    
  • cmd/object-api-utils.go+2 5 modified
    @@ -174,10 +174,7 @@ func IsValidObjectPrefix(object string) bool {
     	// work with file systems, we will reject here
     	// to return object name invalid rather than
     	// a cryptic error from the file system.
    -	if strings.ContainsRune(object, 0) {
    -		return false
    -	}
    -	return true
    +	return !strings.ContainsRune(object, 0)
     }
     
     // checkObjectNameForLengthAndSlash -check for the validity of object name length and prefis as slash
    @@ -199,7 +196,7 @@ func checkObjectNameForLengthAndSlash(bucket, object string) error {
     	if runtime.GOOS == globalWindowsOSName {
     		// Explicitly disallowed characters on windows.
     		// Avoids most problematic names.
    -		if strings.ContainsAny(object, `:*?"|<>`) {
    +		if strings.ContainsAny(object, `\:*?"|<>`) {
     			return ObjectNameInvalid{
     				Bucket: bucket,
     				Object: object,
    
  • cmd/object-api-utils_test.go+56 0 modified
    @@ -19,19 +19,75 @@ package cmd
     
     import (
     	"bytes"
    +	"context"
     	"fmt"
     	"io"
     	"net/http"
    +	"net/http/httptest"
     	"reflect"
    +	"runtime"
     	"strconv"
     	"testing"
     
     	"github.com/klauspost/compress/s2"
    +	"github.com/minio/minio/internal/auth"
     	"github.com/minio/minio/internal/config/compress"
     	"github.com/minio/minio/internal/crypto"
     	"github.com/minio/pkg/trie"
     )
     
    +// Wrapper
    +func TestPathTraversalExploit(t *testing.T) {
    +	if runtime.GOOS != globalWindowsOSName {
    +		t.Skip()
    +	}
    +	defer DetectTestLeak(t)()
    +	ExecExtendedObjectLayerAPITest(t, testPathTraversalExploit, []string{"PutObject"})
    +}
    +
    +// testPathTraversal exploit test, exploits path traversal on windows
    +// with following object names "\\../.minio.sys/config/iam/${username}/identity.json"
    +// #16852
    +func testPathTraversalExploit(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler,
    +	credentials auth.Credentials, t *testing.T,
    +) {
    +	if err := newTestConfig(globalMinioDefaultRegion, obj); err != nil {
    +		t.Fatalf("Initializing config.json failed")
    +	}
    +
    +	objectName := `\../.minio.sys/config/hello.txt`
    +
    +	// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
    +	rec := httptest.NewRecorder()
    +	// construct HTTP request for Get Object end point.
    +	req, err := newTestSignedRequestV4(http.MethodPut, getPutObjectURL("", bucketName, objectName),
    +		int64(5), bytes.NewReader([]byte("hello")), credentials.AccessKey, credentials.SecretKey, map[string]string{})
    +	if err != nil {
    +		t.Fatalf("failed to create HTTP request for Put Object: <ERROR> %v", err)
    +	}
    +
    +	// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
    +	// Call the ServeHTTP to execute the handler.
    +	apiRouter.ServeHTTP(rec, req)
    +
    +	ctx, cancel := context.WithCancel(GlobalContext)
    +	defer cancel()
    +
    +	// Now check if we actually wrote to backend (regardless of the response
    +	// returned by the server).
    +	z := obj.(*erasureServerPools)
    +	xl := z.serverPools[0].sets[0]
    +	erasureDisks := xl.getDisks()
    +	parts, errs := readAllFileInfo(ctx, erasureDisks, bucketName, objectName, "", false)
    +	for i := range parts {
    +		if errs[i] == nil {
    +			if parts[i].Name == objectName {
    +				t.Errorf("path traversal allowed to allow writing to minioMetaBucket: %s", instanceType)
    +			}
    +		}
    +	}
    +}
    +
     // Tests validate bucket name.
     func TestIsValidBucketName(t *testing.T) {
     	testCases := []struct {
    
8d6558b23649

fix: convert '\' to '/' on windows (#16852)

https://github.com/minio/minioHarshavardhanaMar 20, 2023via ghsa
1 file changed · +2 1
  • cmd/generic-handlers.go+2 1 modified
    @@ -22,6 +22,7 @@ import (
     	"net"
     	"net/http"
     	"path"
    +	"path/filepath"
     	"runtime/debug"
     	"strings"
     	"sync/atomic"
    @@ -349,7 +350,7 @@ func hasBadHost(host string) error {
     // Check if the incoming path has bad path components,
     // such as ".." and "."
     func hasBadPathComponent(path string) bool {
    -	path = strings.TrimSpace(path)
    +	path = filepath.ToSlash(strings.TrimSpace(path)) // For windows '\' must be converted to '/'
     	for _, p := range strings.Split(path, SlashSeparator) {
     		switch strings.TrimSpace(p) {
     		case dotdotComponent:
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.