GitHub Advisoryadvisory · crates.io

GHSA-mx42-j6wv-px98 RustFS has an authorization bypass in multipart UploadPartCopy enables cross-bucket object exfiltration

MEDIUM Published 2026-04-08 Modified 2026-04-10 Official page ↗
Aliases
CVE-2026-39360
Ecosystem
crates.io
Packages
rustfs
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weaknesses
CWE-862
Reviewed
2026-04-08

Affected packages

• crates.io rustfs: <= 0.0.2 (no fix yet)

Description

RustFS contains a missing authorization check in the multipart copy path (`UploadPartCopy`). A low-privileged user who cannot read objects from a victim bucket can still exfiltrate victim objects by copying them into an attacker-controlled multipart upload and completing the upload. This breaks tenant isolation in multi-user / multi-tenant deployments. Impact Unauthorized cross-bucket / cross-tenant data exfiltration (Confidentiality: High). An attacker with only minimal permissions on their own bucket (multipart upload + Put/Get on destination objects) can copy and retrieve objects from a victim bucket without having `s3:GetObject` (or equivalent) permission on the source. In the attached PoC, the attacker successfully exfiltrates a 5MB private object and proves integrity via matching SHA256 and size. Threat Model (Realistic) • Victim tenant/user owns a bucket (e.g., `victim-bucket-*`) and stores private objects (e.g., `private/finance_dump.bin`). • Attacker tenant/user has no permissions on the victim bucket: • cannot `ListObjects`, `HeadObject`, `GetObject`, or `CopyObject` from the victim bucket. • Attacker has minimal permissions only on attacker bucket: • `CreateMultipartUpload`, `UploadPart`, `UploadPartCopy`, `CompleteMultipartUpload`, `AbortMultipartUpload`, • and `PutObject`/`GetObject` for objects in attacker bucket. • Despite this, attacker can exfiltrate victim objects via multipart copy. Root Cause Analysis The access control layer fails open for multipart copy-related operations: File: `rustfs/src/storage/access.rs` • `abort_multipart_upload()` returns `Ok(())` without authorization (L435–437) • `complete_multipart_upload()` returns `Ok(())` without authorization (L442–444) • `upload_part_copy()` returns `Ok(())` without authorization (L1446–1448) In contrast, `copy_object()` correctly enforces authorization: • source `GetObject` authorization (L469) • destination `PutObject` authorization (L478) The multipart copy implementation reads the source object directly: File: `rustfs/src/app/multipart_usecase.rs` • `store.get_object_reader(&src_bucket, &src_key, ...)` (L959–962) Because `upload_part_copy()` does not enforce source `GetObject` authorization, the server reads and copies victim data even when the requester lacks permission. Affected Versions • Tested vulnerable on: `main` @ `c1d5106acc3480c275a52344df84633bb6dcd8f0` • Git describe: `1.0.0-alpha.86-3-gc1d5106a` The fail-open authorization behavior for `UploadPartCopy` was introduced in: • Commit: `09ea11c13` (per `git blame` on `rustfs/src/storage/access.rs:1443-1448`) Affected range (recommended wording): • All versions from commit `09ea11c13` through `c1d5106acc3480c275a52344df84633bb6dcd8f0` (and likely any releases containing those commits) until a fix is applied. Package version (Cargo metadata) • `rustfs` crate version in this tree: 0.0.5 (`cargo metadata`) Proof of Concept (PoC) – Real Commands + Verified Results Files Place the PoC script at the repository root: • PoC script: `poc_uploadpartcopy_exfil_v3.sh` • Captured output: `poc_v3_output.txt` • *(Optional)* Redacted debug log: `upload_part_copy_debug_redacted.log` (Authorization/signature redacted) Environment RustFS running locally (Docker is simplest), listening on: • `http://127.0.0.1:9000` Tools: • `awscli`, `jq`, `awscurl` Steps to Reproduce 1) Start RustFS (example): ```bash docker compose -f docker-compose-simple.yml up -d ```` 2. Run the PoC and save output: ```bash chmod +x poc_uploadpartcopy_exfil_v3.sh ./poc_uploadpartcopy_exfil_v3.sh | tee poc_v3_output.txt ``` Attachments • `poc_uploadpartcopy_exfil_v3.sh` • `poc_v3_output.txt` Expected Behavior • Attacker operations against victim bucket should be denied: • `ListObjects` -> AccessDenied • `HeadObject` -> AccessDenied • `GetObject` -> AccessDenied • `CopyObject` -> AccessDenied • `UploadPartCopy` from victim -> attacker multipart should also be denied. Actual Behavior • All direct operations against victim are denied (as expected), • but `UploadPartCopy` succeeds, and attacker retrieves the copied object from attacker bucket. Observed PoC Output Victim uploads a private object: • size: `5,242,880` bytes • sha256: `fda018db1da9d8f4c1b287c75943384a3b4ede391ec156039b6d94e17d6ad68f` Attacker exfiltrates it via multipart copy: • stolen size: `5,242,880` bytes • stolen sha256: `fda018db1da9d8f4c1b287c75943384a3b4ede391ec156039b6d94e17d6ad68f` Proof: • hashes and sizes match (victim == stolen) -> unauthorized cross-bucket read confirmed. Network Evidence (Redacted) The debug log shows a successful request with: • HTTP method: `PUT` • destination: `//?partNumber=1&uploadId=...` • header: `x-amz-copy-source: /private/finance_dump.bin` • response: `HTTP/1.1 200` with `......` Fix Implement authorization checks equivalent to `copy_object()` for multipart copy paths: • `upload_part_copy`: • enforce source `GetObject` authorization on `x-amz-copy-source` • enforce destination `PutObject` authorization on the target object • (recommended) apply the same tag-condition enforcement used by `copy_object()` on the source. • `complete_multipart_upload`: • enforce destination `PutObject` authorization • `abort_multipart_upload`: • enforce appropriate multipart permission (or destination `PutObject` as a safe boundary)

Related

Alias · CVE (1)

  • CVE-2026-39360 RustFS is a distributed object storage system built in Rust. vulnerability

Weakness · CWE (1)

  • CWE-862 Missing Authorization weakness

References