# Pheditor: Terminal command-allowlist bypass via argument injection leads to RCE — surviving vector after the metacharacter-sanitization fixes

Pheditor's terminal allowlist is a prefix match over shell_exec, so allowlisted binaries like find/git/php/tar execute arbitrary commands via their own flags, bypassing metacharacter filters.

- Payout: Research
- Severity: high
- Program: Pheditor
- Found by: anir0y, manus-use, sermikr0, adamyordan
- Disclosed: 2026-07-24
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-g3hq-hphg-8fhh
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-g3hq-hphg-8fhh
- Tags: area:web domain:web impact:rce kind:research platform:ghsa sev:high tech:php technique:default-creds technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** PHP web app terminal action (pheditor.php) invoking shell_exec
**Root cause:** TERMINAL_COMMANDS is enforced as a prefix match with no argument validation, and the allowlist includes binaries (find, git, php, tar, grep) that can execute arbitrary programs via their own options; the metacharacter denylist does not cover argument injection.
**Steps:**
1. Authenticate (default admin/admin per GHSA-p4h7-p9rj-2pq2) and reach the terminal action
2. Send command `find . -maxdepth 0 -exec touch /tmp/PWNED {} +` with any dir
3. Prefix matches allowlisted `find`, contains no rejected metacharacters, reaches shell_exec
4. find -exec runs the arbitrary program, creating /tmp/PWNED
5. Alternates: `git -c alias.x='!touch /tmp/PWNED' x`, `php -r 'system("id");'`, `tar -cf /dev/null --checkpoint=1 --checkpoint-action=exec="touch /tmp/PWNED" .`
**How found:** Noticed the allowlist was a prefix match with no argument validation and that it included binaries capable of executing programs via their own flags; reasoned that argument injection (CWE-88) survives the metacharacter denylist that fixed prior advisories, then built a harness mirroring the three guards to confirm.
**Exploitation:** A caller with terminal permission POSTs a command starting with an allowlisted binary carrying a code-exec flag; no shell metacharacters are needed, so the denylist passes and shell_exec runs it under the web server's privileges. Chained with the hardcoded default admin password, a default deployment gives effectively unauthenticated RCE.
**Escalation:** Chained with GHSA-p4h7-p9rj-2pq2 (hardcoded default admin password, no forced change) to convert authenticated terminal access into unauthenticated RCE on default deployments.
**Insight:** When a command allowlist is a prefix match over a shell sink, audit each allowlisted binary for code-exec flags (-exec, -c/alias !, -r, --checkpoint-action) rather than only hunting shell metacharacters; prior metacharacter fixes leave this class intact.

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

**find -exec payload bypassing allowlist**

```
command = `find . -maxdepth 0 -exec touch /tmp/PWNED {} +`
  dir = (any)

This contains no rejected metacharacter, prefix-matches the allowlisted `find`, and `find -exec` runs an arbitrary program.
```

**Alternative allowlisted-binary RCE payloads**

```
`git -c alias.x='!touch /tmp/PWNED' x`, `php -r 'system("id");'`, `tar -cf /dev/null --checkpoint=1 --checkpoint-action=exec="touch /tmp/PWNED" .`.
```

