Jan 21, 2026·6 min read·13 visits
File Browser versions prior to 2.55.0 failed to use constant-time comparison logic during authentication. Because verifying a password with bcrypt takes significantly longer than checking if a user exists in the database, the server responded much faster for invalid users than for valid ones. This discrepancy allows attackers to accurately map out valid accounts on the system.
A classic timing side-channel vulnerability in the popular File Browser application allows unauthenticated attackers to enumerate valid usernames by measuring the server's response time during login attempts.
In the world of web application security, we often obsess over the data the server explicitly sends back—error messages, stack traces, or JSON payloads. But sometimes, the most damning information isn't what the server says, but how long it takes to say it.
File Browser is a sleek, Go-based file manager that people love for its speed and simplicity. It’s often used to manage sensitive documents, making it a juicy target. However, in its quest for efficiency, the authentication mechanism leaked information through a classic side-channel: time.
This vulnerability (CVE-2026-23849) is a textbook example of a timing attack. It allows an attacker to ask the server, "Does the user 'admin' exist?" and receive a silent "Yes" simply by watching a stopwatch. It’s the digital equivalent of knocking on a door: if someone walks to the peephole to check who it is, you know someone is home, even if they don't open the door. If the house is empty, the silence is immediate.
The root cause lies in a common programming optimization that turns fatal in a security context: the short-circuit logical OR operator (||). Developers are taught to optimize code by failing fast. If a condition is met that invalidates the request, why do more work?
In the vulnerable JSONAuth.Auth function, the code logic was essentially: "If the user lookup fails OR the password check fails, deny access."
Here is the logic in plain English:
The fatal flaw is step 3. File Browser uses bcrypt for password hashing. Bcrypt is intentionally slow. It uses a work factor (cost) to make brute-forcing hard, typically taking 50ms to 500ms depending on the server CPU. A database lookup for a missing user, on the other hand, takes microseconds—maybe 1ms total.
This creates a massive, measurable time gap.
To a hacker, that 50ms difference is as loud as a siren.
Let's look at the Go code responsible for this logic. This snippet is from auth/json.go.
u, err := usr.Get(srv.Root, cred.Username)
// The fatal flaw:
// If err != nil (User not found), the second part is NEVER executed.
if err != nil || !users.CheckPwd(cred.Password, u.Password) {
return nil, os.ErrPermission
}Go's runtime executes the left side of || first. If err != nil, the statement evaluates to true, and the block executes immediately. The expensive users.CheckPwd function is skipped entirely.
The patch introduces a concept known as "Constant Time Execution" (or strictly speaking, normalized execution time). The developers realized they must perform a bcrypt hash even if the user doesn't exist.
// Define a dummy hash to burn CPU cycles if the user is missing
const dummyHash = "$2a$10$O4mEMeOL/nit6zqe.WQXauLRbRlzb3IgLHsa26Pf0N/GiU9b.wK1m"
func (a JSONAuth) Auth(...) {
u, err := usr.Get(srv.Root, cred.Username)
// Normalize the data
hash := dummyHash
if err == nil {
hash = u.Password
}
// ALWAYS perform the expensive check
// If user is missing, we check against dummyHash.
// If user exists, we check against real hash.
match := users.CheckPwd(cred.Password, hash)
if !match || err != nil {
return nil, os.ErrPermission
}
return u, nil
}By forcing the server to crunch numbers regardless of the username's validity, the response times flatten out. The signal is lost in the noise.
Exploiting timing attacks over a network (like the internet) is tricky because of "jitter." Network latency varies. A 50ms spike might just be a router hiccup, not a valid user. To exploit this reliably, we use statistics.
user_xyz123). They measure the average response time and calculate the standard deviation.Mean + (5 * Standard_Deviation). Anything above this is statistically significant.admin, root, backup).A Python script targeting this vulnerability would look something like this:
def measure_response(user):
start = time.perf_counter()
requests.post(target, json={"username": user, "password": "x"})
return time.perf_counter() - start
# 1. Calibrate Baseline (Invalid Users)
baseline_times = [measure_response(random_str()) for _ in range(50)]
threshold = mean(baseline_times) + 0.05 # Add 50ms buffer
# 2. Attack
for user in wordlist:
latency = measure_response(user)
if latency > threshold:
print(f"[+] VALID USER FOUND: {user} ({latency:.4f}s)")> [!NOTE] > In a real-world scenario, smart attackers will average multiple requests for the same username to smooth out network jitter, making the detection of valid accounts nearly 100% accurate.
You might ask, "So what if they know my username? They still need the password."
True, but in security, knowledge is power. Username enumeration is the precursor to more aggressive attacks.
admin user and try 10,000 passwords against just them. It's quieter and more effective.j.smith is a user, I can target John Smith with a specific phishing email regarding his File Browser account.password, 123456, or the username itself.In a system like File Browser, which often sits on the perimeter allowing access to internal files, a compromised account often leads to full data exfiltration or Remote Code Execution (if the user can upload files).
The fix is straightforward: Update to File Browser v2.55.0.
If you cannot update immediately, you must rely on perimeter defenses:
/api/login endpoint. If an attacker can only make 1 request every 5 seconds, the statistical analysis required for a timing attack becomes painfully slow.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
File Browser File Browser | < 2.55.0 | 2.55.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-208 |
| Attack Vector | Network |
| CVSS | 5.3 (Medium) |
| EPSS Score | 0.0009 |
| Impact | Information Disclosure (Username Enumeration) |
| Exploit Status | PoC Available |
Observable Timing Discrepancy