ekofyi
SeaweedFS Bucket Isolation Breaks Through X-Amz-Copy-Source
Security Research9 min read

SeaweedFS Bucket Isolation Breaks Through X-Amz-Copy-Source

A path traversal flaw in SeaweedFS lets a caller with access to one bucket copy objects from other buckets through the S3 gateway. Here is how the confused-deputy bypass works and what operators should do.

SeaweedFS has a path traversal vulnerability in its S3 gateway that breaks one of the most important promises object storage makes: bucket isolation.

The issue, tracked as CVE-2026-55874 and GHSA-56wq-x3wv-3ff4, affects the X-Amz-Copy-Source header used by the S3 CopyObject and UploadPartCopy operations. A caller authorized to read and write only one bucket can use traversal segments in that header to copy an object from another bucket into the bucket they control.

The advisory was published on August 28, 2026 — yesterday, relative to today. The practical response is straightforward: upgrade to SeaweedFS 4.34 or later. The interesting part is why the earlier fix was not enough.

The authorization check and the file read disagree

The vulnerable behavior is a classic confused-deputy authorization bypass.

A caller sends a copy request whose destination is in a bucket they are allowed to control. SeaweedFS evaluates the caller's IAM policy against that destination bucket, which makes the request appear authorized. But the source object is taken from the X-Amz-Copy-Source header, and that value can contain a .. path segment.

That segment survives into the server-side filer path and resolves to a different bucket.

In other words, the gateway authorizes one path and reads another.

Consider a caller with Read and Write access only to bucket-a. The request targets bucket-a, so the IAM check passes. The copy source is shaped like this:

bucket-a/../victim-bucket/key

The gateway resolves the source as the object key in victim-bucket, then writes the copied result into a destination controlled by the caller in bucket-a. Once the object is in bucket-a, the caller can read it through their ordinary permissions.

That is the entire security boundary failure. No direct permission to victim-bucket is required.

The dangerous operation is not necessarily a direct read. A permitted copy operation can become an unintended read primitive when authorization and path resolution use different interpretations of the request.

Why the 4.30 fix did not close the issue

This is the detail worth paying attention to if you maintain gateways, storage APIs, or internal authorization layers.

SeaweedFS hardened the request URL path against traversal in version 4.30 as part of CVE-2026-54917. That fix addressed traversal in the URL path. It did not apply the same protection to the X-Amz-Copy-Source header.

The header was checked for emptiness, but not for traversal segments. The result was an uneven security boundary: one representation of a path was validated, while another representation of a path continued into the backend.

That pattern shows up everywhere in real systems. A request can carry resource identity in the URL, a header, a query parameter, a JSON field, or an encoded value nested inside another protocol. Fixing traversal in one location does not mean traversal is fixed across the request lifecycle.

The right question is not, "Did we sanitize the path?"

It is, "Where can an attacker still express the resource path, and do all of those paths pass through the same validation and authorization logic?"

The attack chain

The exploit path is relatively simple:

  1. The caller has access to one bucket, including permission to read and write objects there.
  2. The caller submits CopyObject with a destination in that authorized bucket.
  3. The X-Amz-Copy-Source header contains a traversal segment.
  4. IAM evaluates the request against the bucket named by the request URL.
  5. The backend filer resolves the traversed source path into another bucket.
  6. SeaweedFS copies the object into the caller's bucket.
  7. The caller reads the copied object normally.

The important point is that the attacker does not need to guess a way around the destination authorization check. They operate entirely inside an operation they are already allowed to perform and manipulate the source path used by the server.

UploadPartCopy, implemented through CopyObjectPartHandler, is affected by the same vector. Multipart copy support is not a safe alternative here; it reaches the same class of source-path handling and must receive the same validation.

Who is affected?

All SeaweedFS releases prior to 4.34 are affected according to the advisory.

Version 4.30 is not sufficient protection for this issue. It contains the earlier fix for CVE-2026-54917, which hardened the request URL path, but the X-Amz-Copy-Source header remained insufficiently validated.

SeaweedFS 4.34 and later contain the fix for this vulnerability. The patch is associated with SeaweedFS issue #9929 and commit b44cf51fe931bd75aa4d37ae766bea90d7f85ccd.

The vulnerability was reported responsibly by @47Cid.

What the patch changes

The patched implementation validates both the copy-source bucket and object key using the same IsValidBucketName and IsValidObjectKey guards already applied to the request URL.

That consistency matters more than the specific helper names. The fix closes the gap by applying the relevant path validation before the copy handler runs, and it applies the same check to UploadPartCopy.

This is the design lesson: resource identifiers should pass through one well-defined validation policy regardless of which protocol field carries them.

If the URL path gets one set of rules and the copy-source header gets another, the attacker will look for the less-protected representation. They only need one path into the backend's namespace.

What operators should do now

1. Upgrade to 4.34 or later

This is the primary remediation. The advisory lists no configuration workaround that turns the vulnerable behavior off safely.

If your deployment is on a release earlier than 4.34, treat the S3 gateway as affected even if it already runs version 4.30 or another release containing the URL-path fix.

2. Add a temporary reverse-proxy rule if you cannot upgrade

For deployments that cannot upgrade immediately, place the gateway behind a reverse proxy that rejects requests when the X-Amz-Copy-Source header contains any of the following patterns:

  • ..
  • %2e%2e
  • Backslash sequences

This is a compensating control, not a replacement for the patch. Header normalization and decoding behavior can differ between a reverse proxy and the application. A filter that looks only for one textual form may miss another representation, or it may normalize the value differently from SeaweedFS.

Use the proxy rule to reduce exposure while scheduling the upgrade. Do not treat it as proof that the underlying authorization issue has been fixed.

3. Review bucket permissions

The exploit requires a caller with read and write access to a bucket they control. That permission combination is normal for many applications, but it is still worth checking where it exists and why.

Look for identities that can both:

  • Write objects to a bucket.
  • Read objects from that bucket.
  • Invoke copy operations through the S3 gateway.

Reducing permissions may limit some exposure, but it does not replace upgrading. The vulnerability is in the gateway's handling of a permitted copy operation, and permission changes can be difficult to reason about across every identity and workload.

4. Review copy activity where logs allow it

If you retain S3 gateway or reverse-proxy logs, look for unusual CopyObject or UploadPartCopy requests and inspect their X-Amz-Copy-Source values. Traversal indicators in that header are especially relevant:

  • Literal .. segments.
  • Encoded dot segments such as %2e%2e.
  • Backslash-based path forms.
  • Copy operations where the source and destination buckets do not match the caller's expected workflow.

The source material does not specify a log field layout or detection rule for SeaweedFS deployments, so the exact query depends on how your gateway logs headers and S3 operations. The goal is to identify copy requests whose source path attempts to move outside the expected bucket namespace.

Do not assume that an absence of suspicious logs proves the vulnerability was not exploited. Logging may omit headers, copy operations may be normal application traffic, and copied data can look legitimate once it lands in an authorized bucket.

The broader engineering failure

This vulnerability is a reminder that path traversal is not only a web-server problem.

Object storage APIs frequently model bucket and object names as strings. Internally, those strings may be converted into filesystem-like paths, filer keys, database identifiers, or routing targets. The API may call them object keys, but the backend still has to interpret them somewhere. If path semantics leak into that interpretation, traversal becomes relevant even when the public API is not supposed to expose a filesystem.

The more subtle issue is the separation between authorization identity and execution identity.

IAM evaluated the bucket named in the request URL. The copy operation executed against a source path reconstructed from another request field. Those two values were related, but not securely bound together. The authorization layer said, "this caller may operate on bucket-a," while the storage layer effectively heard, "read from whichever bucket this path resolves to."

That mismatch is what makes this a confused-deputy bug rather than a simple missing permission check.

A secure design should make the authorization decision over the same canonical resource that the backend will access. If a request has both a destination and a source, both need explicit validation, canonicalization, and authorization. Validating only the destination is not enough because the source is where the sensitive read happens.

A useful review checklist for copy APIs

When reviewing an object-storage gateway or any API with server-side copy behavior, I would ask:

  • Can the source resource be supplied in a header or secondary request field?
  • Is that source parsed using the same rules as the URL path?
  • Are .., encoded dot segments, and backslashes rejected consistently?
  • Does canonicalization happen before authorization, and does authorization use the canonical value?
  • Can a caller copy from a resource they cannot read directly?
  • Are multipart-copy handlers protected by the same checks as ordinary copy handlers?
  • Can the backend resolve bucket and object names as filesystem-like paths?
  • Are source and destination logged in a way that supports investigation?

These questions apply well beyond SeaweedFS. Any system that lets a caller ask the server to move, copy, import, or transform a resource should be reviewed for this same split-brain behavior.

The fix is small; the boundary is not

The remediation for SeaweedFS is clear: upgrade to 4.34 or later. If that is not immediately possible, reject dangerous X-Amz-Copy-Source values at the reverse proxy and treat the environment as exposed until the upgrade is complete.

But the more durable takeaway is architectural. Security checks must follow the data, not just the obvious request field. The URL is not the whole request. Headers, query parameters, multipart fields, and encoded secondary paths can all carry resource identity.

A patch that protects one representation while leaving another unchecked may make the vulnerability look fixed in a quick review. Attackers do not review the patch that way. They trace every representation that reaches the backend.

So should defenders.

Related posts

Written by Eko

If you found this useful, follow @ekofyi on X for more notes like this — or get in touch if you have a problem to solve.