CVE-2026-39360 — rustfs
MEDIUMCVE-2026-39360 is a medium-severity (CVSS 4.3) CWE-862 vulnerability in rustfs. No vendor fix is recorded yet; mitigation options are listed below.
RustFS has an authorization bypass in multipart UploadPartCopy enables cross-bucket object exfiltration
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-39360.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-39360 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,238 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
rustfsReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.
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, orCopyObjectfrom the victim bucket.
- cannot
- Attacker has minimal permissions only on attacker bucket:
CreateMultipartUpload,UploadPart,UploadPartCopy,CompleteMultipartUpload,AbortMultipartUpload,- and
PutObject/GetObjectfor 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()returnsOk(())without authorization (L435–437)complete_multipart_upload()returnsOk(())without authorization (L442–444)upload_part_copy()returnsOk(())without authorization (L1446–1448)
In contrast, copy_object() correctly enforces authorization:
- source
GetObjectauthorization (L469) - destination
PutObjectauthorization (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(pergit blameonrustfs/src/storage/access.rs:1443-1448)
Affected range (recommended wording):
- All versions from commit
09ea11c13throughc1d5106acc3480c275a52344df84633bb6dcd8f0(and likely any releases containing those commits) until a fix is applied.
Package version (Cargo metadata)
rustfscrate 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
- Start RustFS (example):
docker compose -f docker-compose-simple.yml up -d
- Run the PoC and save output:
chmod +x poc_uploadpartcopy_exfil_v3.sh
./poc_uploadpartcopy_exfil_v3.sh | tee poc_v3_output.txt
Attachments
Expected Behavior
-
Attacker operations against victim bucket should be denied:
ListObjects-> AccessDeniedHeadObject-> AccessDeniedGetObject-> AccessDeniedCopyObject-> AccessDenied
-
UploadPartCopyfrom victim -> attacker multipart should also be denied.
Actual Behavior
- All direct operations against victim are denied (as expected),
- but
UploadPartCopysucceeds, and attacker retrieves the copied object from attacker bucket.
Observed PoC Output
Victim uploads a private object:
- size:
5,242,880bytes - sha256:
fda018db1da9d8f4c1b287c75943384a3b4ede391ec156039b6d94e17d6ad68f
Attacker exfiltrates it via multipart copy:
- stolen size:
5,242,880bytes - 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:
/<attacker-bucket>/<dst-key>?partNumber=1&uploadId=... - header:
x-amz-copy-source: <victim-bucket>/private/finance_dump.bin - response:
HTTP/1.1 200with<CopyPartResult><ETag>...</ETag>...</CopyPartResult>
Fix
Implement authorization checks equivalent to copy_object() for multipart copy paths:
-
upload_part_copy:- enforce source
GetObjectauthorization onx-amz-copy-source - enforce destination
PutObjectauthorization on the target object - (recommended) apply the same tag-condition enforcement used by
copy_object()on the source.
- enforce source
-
complete_multipart_upload:- enforce destination
PutObjectauthorization
- enforce destination
-
abort_multipart_upload:- enforce appropriate multipart permission (or destination
PutObjectas a safe boundary)
- enforce appropriate multipart permission (or destination
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | rustfs | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for rustfs, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Remediation status
No patched version of rustfs has shipped for CVE-2026-39360 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.
Mitigate without a patch
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-39360 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-39360. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-39360 in your dependencies?
O3 Security finds CVE-2026-39360 across crates.io dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.