Cockpit Unauthenticated Remote Code Execution via SSH Argument Injection (CVE-2026-4631)
by cyberheartmi9 (analysis); reported by Jelle van der Waa · 2026-07-05
- Severity
- Critical
- CVE
- CVE-2026-4631 (GHSA-m4gv-x78h-3427)
- Category
- web
- Affected product
- Cockpit (Linux web-based server admin console)
- Affected versions
- 327 – 359 (fixed in 360)
- Disclosed
- 2026-07-05
- Patch status
- patched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-04 |
| Author / Researcher | cyberheartmi9 (analysis); reported by Jelle van der Waa |
| CVE / Advisory | CVE-2026-4631 (GHSA-m4gv-x78h-3427) |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.8 |
| Status | PoC |
| Tags | cockpit, ssh, command-injection, argument-injection, cwe-78, unauthenticated-rce, proxycommand |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Cockpit (Linux web-based server admin console) |
| Versions Affected | 327 – 359 (fixed in 360) |
| Language / Platform | Python 3 (attack script), target is C (cockpit-ws) + Python (cockpit.beiboot) |
| Authentication Required | No |
| Network Access Required | Yes (HTTP access to Cockpit’s port 9090) |
Summary
Cockpit’s remote-login feature passes attacker-controlled hostnames (from the URL path) and usernames (from the Authorization: Basic header) directly to the OpenSSH ssh client without validation or a -- end-of-options separator. Because both values are used before credential verification completes, an unauthenticated attacker can craft a hostname beginning with -oProxyCommand=<cmd> to make ssh treat it as an option rather than a destination, or embed shell metacharacters in the username to exploit SSH’s %r token expansion in a Match exec directive — either path leads to arbitrary command execution as the cockpit-ws process user.
Vulnerability Details
Root Cause
Starting in Cockpit 327, the C cockpit-ssh binary was replaced by python3 -m cockpit.beiboot, which invokes the system ssh client. Neither cockpit-ws (src/ws/cockpitauth.c) nor cockpit.beiboot (src/cockpit/beiboot.py), nor the bundled ferny library, inserts a -- separator before the destination argument, so a hostname/username starting with - is parsed by ssh/Python argparse as an option instead of positional data (compounded by CPython argparse bug #66623).
Attack Vector
- Send
GET /cockpit+=-oProxyCommand=<COMMAND>/loginto the Cockpit web service on port 9090 with anyAuthorization: Basicvalue. cockpit-wsextracts-oProxyCommand=<COMMAND>from the URL path and passes it unsanitized as the “hostname” tocockpit.beiboot.beiboot.via_ssh()buildsssh -oProxyCommand=<COMMAND> <fake-host>with no--separator, so SSH parses-oProxyCommand=<COMMAND>as a real option.- SSH executes
<COMMAND>as its ProxyCommand while attempting to resolve the bogus hostname — before any authentication succeeds. - Alternatively, an attacker sets the
Authorization: Basicusername to a value likex; touch /tmp/pwned; #, which is expanded via SSH’s%rtoken in a target’sMatch execdirective, executing the injected shell command.
Impact
Unauthenticated, pre-auth remote code execution as the user running cockpit-ws (typically root or a privileged service account), on any exposed Cockpit instance running versions 327–359.
Environment / Lab Setup
Target: Cockpit 327-359 (cockpit-ws) exposed on TCP/9090, OpenSSH < 9.6 on the host
Attacker: python3 exploit.py --target http://<target>:9090/ --vector username|hostname --cmd "<command>"
Proof of Concept
PoC Script
See
exploit.pyin this folder.
| |
Sends the crafted HTTP request(s) to the Cockpit login endpoint using either the hostname (ProxyCommand) or username (%r token) injection vector, supports scanning single/multiple targets, and can use an out-of-band callback for blind detection.
Detection & Indicators of Compromise
GET /cockpit+=-o[A-Za-z]+=.*/login
GET /cockpit+=-[A-Za-z].*/login
journalctl -u cockpit-ws | grep -E "beiboot|ProxyCommand|-oProxy"
journalctl _COMM=ssh | grep -v "^--$"
Signs of compromise:
- HTTP requests to Cockpit’s
/cockpit+=.../loginendpoint where the path segment contains SSH option syntax (-o...=) or semicolons appear in the decodedAuthorization: Basicvalue - Unexpected
ssh/beibootchild processes spawned bycockpit-wswith anomalous arguments
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to Cockpit 360+, which adds a -- end-of-options separator before hostnames/destinations in beiboot.py, cockpitauth.c, and the bundled ferny library |
| Interim mitigation | Set LoginTo = false under [WebService] in /etc/cockpit/cockpit.conf to disable the remote-login feature entirely |
References
Notes
Mirrored from https://github.com/cyberheartmi9/CVE-2026-4631-cockpit-RCE on 2026-07-05.
| |