How CVE-2026-96326 Stores Malicious HTML in Every Entry View
HT Contact Form's public submission route (/wp-json/ht-form/v1/submission, POST) registers a permission callback that returns true unconditionally, so any unauthenticated scripted client can submit field values exactly like a form. A field configured as type richtext passes the attacker's HTML string straight into sanitize_richtext_field(). Version 2.10.2 gates that value with wp_kses() plus regex rewrites that only recognize double-quoted style and class attributes; markup crafted so kses never sees a tag or attribute boundary passes through untouched, while a browser's HTML parser re-associates the same bytes into a live event-handler attribute. The value is stored via Entries::create() and re-emitted as HTML in notification emails and admin entry views — the render-side wp_kses_post() filter shares kses' parsing engine, so the evasion survives output filtering too. Script then executes in the browser of anyone who accesses an injected page.
BitFire FREE Stops the Attack Before WordPress Runs
Both applicable defenses ship in BitFire FREE for eligible non-commercial sites — no upgrade required for this CVE. Because the exploit is delivered as an unauthenticated scripted POST to a form endpoint, BitFire Bot Protection classifies it as automated form traffic and blocks it outright before any vulnerable plugin code executes; scripts and fake browsers also fail BitFire's JavaScript browser verification. Only a client explicitly allowlisted by the site owner or a fully verified real browser gets past that layer. If a crafted request does reach the filter, BitFire's WAF inspects the POST body, including form fields, and discards requests carrying cross-site scripting payloads such as event-handler-bearing markup before the vulnerable sanitizer or the database ever sees them. Delivery blocked at the bot layer, content blocked at the WAF layer: the stored XSS chain never completes.
The 2.10.3 Patch Replaces Guessing with Parsing
The vendor rebuilt sanitize_richtext_field() in admin/Includes/Api/Endpoints/Submission.php. Instead of trusting kses' regex-style tag splitting, 2.10.3 parses submitted HTML with libxml's DOM parser — the same parsing model a browser applies — then walks the resulting tree with the new sanitize_richtext_dom_node() allow-list. Script, style, iframe, object, embed, svg, math, template, noscript, and form tags are dropped with their content, unknown tags are unwrapped, and every attribute not explicitly allowed for its tag is removed, which ends event-handler survival regardless of quoting. Surviving href, target, rel, class, and style values are rebuilt from validated tokens, output is serialized from the sanitized DOM, and hosts without DOMDocument fall back to wp_strip_all_tags(). The shared Draft.php save/resume flow inherits the same hardening. Update to 2.10.3 or later and confirm the HTCONTACTFORM_VERSION constant or plugin header.
If Your Site Was Affected, Investigate for Persistence.
Patching closes the known path; it does not undo a compromise that already succeeded. Every site that ran 2.10.2 or earlier should assume attacker markup could already be sitting in stored entries and notification emails. Patch immediately, then investigate for compromise: audit stored Rich Text entries for script-bearing HTML and review which users viewed injected pages. Run BitFire Threat Hunter for a thorough post-compromise sweep that uncovers backdoor WordPress administrator accounts, hidden database triggers, long-running PHP processes, and droppers that can restore malware or reinfect the site. Remove every persistence mechanism found, rotate administrator and application credentials, and treat a clean-looking entries list as inconclusive — absence of an obvious malicious file is not proof of a clean site.
Put BitFire in Front of Every Form
CVE-2026-96326 is a reminder that sanitizer bugs in popular form plugins become stored XSS on thousands of sites overnight. BitFire FREE stopped this attack at two independent points — Bot Protection blocking the scripted submission before WordPress ran, and the WAF discarding its XSS payload — without needing a CVE-specific virtual patch. Update HT Contact Form to 2.10.3 now, keep BitFire enabled in front of your forms, and run Threat Hunter if you operated an older release. Deploy BitFire today and stop stored XSS before it reaches your database.