
WordPress Core 4.7.0–7.1.1에서 이중 인코딩된 pagename 트래버설을 통한 CVE-2026-87902 비인증 LFI를 스캔하며, 선택적 PEAR config-create RCE 에스컬레이션 및 대량 스캐닝을 지원합니다.
WordPress 코어 get_page_template() / locate_template() 경로 순회 익스플로잇 도구
승인된 보안 테스트 전용입니다. 컴퓨터 시스템에 대한 무단 접근은 불법입니다.
| 항목 | 세부 정보 |
|---|---|
| CVE ID | CVE-2026-87902 |
| GHSA ID | GHSA-7hp8-65ch-5whp |
| 취약점 유형 | CWE-98 — Include/Require용 파일명의 부적절한 제어 |
| CVSS v3.1 | 8.1 (높음) / CVSS 4.0: 9.2 (치명적) |
| 영향받는 구성 요소 | WordPress 코어 |
| 영향받는 버전 | 4.7.0 ~ 7.1.1 (모든 브랜치) |
| 수정된 버전 | 7.1.2 (및 4.7.37까지의 백포트) |
| 인증 필요 여부 | 없음 (비인증) |
| 공격 벡터 | 단일 GET 요청 |
| 발견자 | Robert Ressl (WordPress HackerOne 프로그램을 통해) |
| 공개 날짜 | 2026년 9월 22일 |
| 실제 환경에서 악용됨 | 예 — CISA KEV 카탈로그에 등재됨 |
CVE-2026-87902는 WordPress 코어의 비인증 경로 순회 및 로컬 파일 포함(LFI) 취약점으로, 2016년 이후 출시된 모든 WordPress 버전(4.7.0 ~ 7.1.1)에 영향을 미칩니다. 공격자는 계정이나 인증이 필요하지 않습니다. 악의적으로 조작된 pagename 쿼리 매개변수를 통해 공격자는 WordPress의 페이지 템플릿 해석 메커니즘이 활성 테마 디렉터리 외부의 읽을 수 있는 모든 .php 파일을 포함하도록 유도할 수 있습니다. 특정 서버 조건(PHP register_argc_argv = On 및 읽을 수 있는 pearcmd.php)에서는 취약점이 원격 코드 실행(RCE)으로 확대될 수 있습니다.
| 브랜치 | 수정된 버전 |
|---|---|
| 4.7.x | 4.7.37 |
| 5.0.x | 5.0.29 |
| 5.3.x | 5.3.25 |
| 5.9.x | 5.9.18 |
| 6.0.x | 6.0.16 |
| 6.6.x | 6.6.9 |
| 6.9.x | 6.9.9 |
| 7.0.x | 7.0.6 |
| 7.1.x | 7.1.2 |
취약점은 wp-includes/template.php의 get_page_template()에 존재합니다. pagename 쿼리 변수가 존재할 때, 이 함수는 page-{$pagename}.php를 구성하지만 디렉터리 순회를 확인하기 위해 해당 값에 validate_file()을 호출하지 않습니다. 그런 다음 후보 파일명이 locate_template()에 전달되며, 이 함수는 이를 테마 디렉터리 경로와 연결하고 file_exists()만 확인합니다 — 해석된 경로가 테마 디렉터리 내부에 머무는지 검증하지 않습니다. 따라서 ../를 포함하는 템플릿 이름은 활성 테마 외부의 읽을 수 있는 PHP 파일로 해석되어 포함 및 실행될 수 있습니다.
GET /?page_id=&pagename= → WP_Query resolves page_id, matches a published page (avoids 404) → get_page_template() urldecode()s pagename, builds page-{$pagename}.php → locate_template() concatenates candidate to theme directory, resolves via file_exists() → template-loader executes realpath() + include
WordPress는 pagename에 sanitize_title_for_query()를 적용하며, 이 함수는 순회를 차단하기 위해 리터럴 점(.)을 하이픈(-)으로 대체합니다. 이는 이미 한 번 디코딩된 입력에 대해 작동하며 재귀적으로 디코딩하지 않습니다. 공격자는 %252e%252e%252f(이중 인코딩된 ../)를 전송합니다:
%252e → %2e, %252f → %2fsanitize_title_for_query()는 %2e%2e%2f(리터럴 점 없음)를 보고 그대로 통과시킴get_page_template()이 내부적으로 urldecode()를 다시 호출: %2e%2e%2f → ../page-로 시작하는 최상위 디렉터리(예: page-templates/)가 존재해야 합니다.page_id로 제공할 수 있는 ID를 가진 게시된 페이지가 최소 하나 존재해야 합니다..php로 끝나야 합니다(WordPress가 확장자를 추가함).Windows 호스트 참고: Win32는 ..를 어휘적으로 정규화하므로 page- 디렉터리가 존재하지 않더라도 순회가 작동할 수 있습니다. Linux에서는 해당 디렉터리가 실제로 존재해야 합니다.
조건:
register_argc_argv가 On으로 설정됨pearcmd.php가 존재함공격자는 LFI를 통해 pearcmd.php를 포함시키고, 해당 파일의 config-create 명령을 악용하여 서버에 PHP 파일을 작성한 뒤, 다시 LFI를 통해 그 파일을 포함시켜 임의 명령을 실행합니다.
CVE-2026-87902는 스위스에 거주하는 보안 연구원 Robert Ressl에 의해 발견되고 보고되었습니다.
2026년 7월 WordPress HackerOne 프로그램을 통해 보고되었습니다. WordPress 보안 팀이 수정 작업을 조율하여 2026년 9월 22일 WordPress 7.1.2와 함께 릴리스했습니다.
| 날짜 | 사건 |
|---|---|
| 2026년 7월 | Robert Ressl이 WordPress HackerOne 프로그램을 통해 취약점 보고 |
| 2026년 9월 22일 | 수정 사항이 포함된 WordPress 7.1.2 릴리스; 보안 권고문 게시 |
| 2026년 9월 22일 | 브랜치 7.0부터 4.7까지 보안 백포트 릴리스 |
| 2026년 9월 23일 | 공격자들이 취약한 사이트 탐색 시작 |
| 2026년 9월 25일 | CISA가 CVE-2026-87902를 KEV 카탈로그에 추가 |
| 기능 | 설명 |
|---|---|
| 버전 핑거프린팅 | meta generator, RSS 피드, OPML, readme.html, 에셋 ?ver=를 통해 WordPress 버전 식별 |
| 스마트 버전 판정 | 수정 버전 매핑을 사용하여 대상이 영향 범위에 있는지 판단 |
| 테마 탐색 | 활성 테마 슬러그 추출, 상위 테마 감지, page-* 디렉터리 탐색 |
| 페이지 ID 탐색 | REST API, page-id- body 클래스, 단축 링크를 통해 유효한 페이지 ID 탐색 |
| LFI 오라클 검증 | wp-links-opml.php 및 wp-trackback.php와 같은 안전한 파일을 부정 통제와 함께 오라클로 사용 |
| 이중 인코딩 트래버설 | sanitize_title_for_query()를 우회하기 위해 /와 .를 자동으로 이중 퍼센트 인코딩 |
| PEAR RCE 익스플로잇 | 일반적인 PEAR 경로를 시도하고 config-create를 통해 마커 파일을 작성 및 실행 |
| 대량 스캔 | 대상 목록의 멀티스레드 동시 스캔 |
| 결과 출력 | 결과를 vuln_lfi.txt 및 vuln_lfitorce.txt에 저장 |
pip install requests
python3 CVE-2026-87902.py
대화형 메뉴:
[1] 단일 스캔 LFI [2] 단일 스캔 LFI + RCE [3] 대량 스캔 LFI [4] 대량 스캔 LFI + RCE [5] 종료
[1]을 선택하고 대상 URL을 입력합니다:
url > https://target.example.com
[2]를 선택하고 URL과 명령을 입력합니다 (기본값 id):
url > https://target.example.com cmd > id
[3] 또는 [4]를 선택하고 대상 URL이 포함된 파일을 입력합니다 (한 줄에 하나씩, #은 주석):
file > targets.txt
| 파일 | 내용 |
|---|---|
| vuln_lfi.txt | 확인된 LFI 대상 |
| vuln_lfitorce.txt | RCE에 성공한 대상 |
TIMEOUT = 8 # 요청 타임아웃 (초) THREADS = 200 # 대량 스캔을 위한 동시 스레드 수 USE_ALL_ORACLES = False # 모든 오라클 사용 (더 정확하지만 느림)
pagename 파라미터에 .., %2e, %252e가 포함된 요청을 차단하십시오page-* 디렉터리가 포함되어 있는지 확인하십시오register_argc_argv 설정을 확인하십시오이 도구는 승인된 보안 테스트 및 교육 목적으로만 사용하도록 의도되었습니다. 사용자는 대상 시스템에 대한 명시적인 서면 승인을 받았는지 확인해야 합니다. 무단 사용은 CFAA, 영국 컴퓨터 오용법 및 기타 국가의 유사 법률을 위반할 수 있습니다. 작성자는 어떠한 오용에 대해서도 책임을 지지 않습니다.