
Generates a self-submitting HTML trigger page that exploits a reflected HTML injection in WordPress login (CVE-2026-64638) to display a custom banner, for authorized security testing.
A single-file, dependency-free PoC generator for the WordPress CVE-2026-64638
reflected HTML-injection on wp-login.php (strip_tags() vs. KSES parser
differential). It produces a self-submitting trigger page: when the victim opens
the file, the page silently POSTs the injection payload and the victim lands
directly on the target login page with a banner rendered — no interaction, no
JavaScript on the target side.
For authorized security testing only. Use only against systems you own or are explicitly authorized to test.
| Versions | Status |
|---|
| 6.4.0 – 7.0.2 | Vulnerable — fires out of the box |
| 5.8 – 6.3 | Conditional — requires a plugin passing login_errors through wp_kses() with style allowed |
| 4.7 – 5.7 | Not exploitable (username is not reflected) |
| 6.4.9 / 6.5.9 / 6.6.6 / 6.7.6+ / 6.8.7 / 6.9.6 / 7.0.3+ | Patched (esc_html) |
python3 banner_poc.py \
--out trigger.html \
--target http://192.0.2.10/wp-login.php \
--msg "Scheduled System Maintenance"
# sub-directory + hidden login path + subtitle
python3 banner_poc.py \
--out trigger.html \
--target http://10.0.0.5:8080/blog/my-secret-login \
--msg "Please sign in again" --sub "Session expired"
Host trigger.html anywhere. When the victim opens it, they are redirected to
the target login page with the banner rendered. The hop is silent: no visible
intermediate content, background matches the WordPress login screen, lands in
~0.5 s.
| Flag | Description |
|---|---|
--out | Output HTML file (required) |
--target | Full login-page URL — sub-directory / hidden login paths included (required) |
--msg | Banner text (default: Scheduled System Maintenance) |
--sub | Optional subtitle line |
wp-login.php reflects a failed-login username into the error message.
Vulnerable versions sanitize it with PHP strip_tags(), which passes
anything where < is followed by whitespace.< div style="..."> arrives as a real element.< /strong> (slash preceded by a space) also survives strip_tags() and is
rebuilt into a real closing tag, letting the payload close the template's
<strong> (an HTML5 active formatting element) as its first token and
keep exact DOM control.div swallows the template's tail
text ("… is not registered on this site …").class-free inline styles that survive
safecss_filter_attr(). On 6.3+ / 6.4+ position:fixed renders a full-page
overlay; on 5.8–6.2 those properties are stripped and the same payload
self-degrades to an in-flow red banner.Pure HTML/CSS — no JavaScript executes on the target. WAF rules targeting
<script> or event handlers do not apply.
This tool is published for defensive research and authorized penetration testing. The author is not responsible for misuse.