CVE-2026-93303 vulnerability and BitFire protection

How BitFire Blocks CVE-2026-93303: HT Contact Form XSS

WordPress vulnerability research

BitFire's WAF detects and blocks the unauthenticated stored DOM-based XSS payload in HT Contact Form ≤ 2.10.1 before WordPress processes the request.

Unauthenticated High severity — CVSS 7.2 Stored XSS script execution Stored DOM-based cross-site scripting
BitFire · Vulnerability advisoryResearch published
AdvisoryCVE-2026-93303
ComponentHT Contact Form – Drag & Drop Form Builder for WordPress
Relevant sourceadmin/Includes/Api/Endpoints/Draft.php (save_draft/update_draft); DOM sink assets/js/form.js (_populateFormFields)
Executive summary

What WordPress administrators need to know

HT Contact Form ≤ 2.10.1 lets unauthenticated visitors store arbitrary HTML in a form draft through POST /wp-json/ht-form/v1/draft/save, whose permission callback is __return_true and whose wp_rest nonce is issued to every visitor on any page that renders a form. Rich Text draft values skip the sanitization the final-submission path applies, and the save response hands back the draft key and access token needed to craft a resume link. Anyone who opens that link loads the stored HTML straight into the Quill editor DOM — container._quill.root.innerHTML = value — executing attacker script in the site origin within the victim's session. BitFire FREE's WAF blocks the malicious form payload before WordPress processes it, and BitFire Threat Hunter covers post-compromise investigation.

At a glance

Key facts

  • Unauthenticated REST access: POST /wp-json/ht-form/v1/draft/save registers __return_true as its permission callback, and the required wp_rest nonce is localized onto every page that renders a form.
  • In 2.10.1, Rich Text draft values bypassed sanitization, while the final-submission path in Submission.php sanitized the same field type.
  • The save response returns draft_key and access_token, letting the drafter build a resume link a victim must click — exploitation requires that user interaction.
  • Client sink: assets/js/form.js assigns the stored draft value via container._quill.root.innerHTML, executing it in the victim's browser.
  • Fixed in 2.10.2 with Submission::sanitize_richtext_field() on all draft write paths and Quill clipboard parsing at the client sink.
  • BitFire FREE's WAF cross-site scripting inspection blocks the malicious form payload before WordPress processes it.
01
Vulnerability overview

Understand the exposure

The affected component, attack path, and practical risk for WordPress websites.

Affected componentHT Contact Form – Drag & Drop Form Builder for WordPress
Potential reach10,000+ installations
Attack techniquestored dom-based cross-site scripting
Published2026-09-24
BitFire's WAF inspects every form field and POST body before WordPress runs — the malicious Rich Text payload behind CVE-2026-93303 is stopped at the request layer, so it is never stored and never reaches a victim's browser.
02
Technical analysis

How the vulnerability works

Research details, affected versions, exploitation behavior, and remediation guidance.

How CVE-2026-93303 Works

HT Contact Form's Save & Resume feature exposes an unauthenticated REST endpoint, POST /wp-json/ht-form/v1/draft/save, registered with __return_true as its permission callback. The wp_rest nonce it requires is localized onto every page that renders a form, so it is an anti-CSRF check, not authentication. In 2.10.1, the save_draft() and update_draft() handlers persist the form_data parameter verbatim — including Rich Text field HTML — while the final-submission path sanitizes it. The save response returns draft_key and access_token, so the attacker can build a resume link. When any user opens that link, assets/js/form.js loads the draft and assigns the stored value straight into the Quill editor DOM via container._quill.root.innerHTML = value, executing attacker script in the site origin inside the victim's session.

BitFire WAF: Blocked Before WordPress Runs

BitFire FREE's Web Application Firewall inspects form fields and POST bodies before WordPress or any plugin processes them, and its cross-site scripting rule class matches the documented payload for CVE-2026-93303 exactly: script-capable HTML submitted in a form_data Rich Text field. The exploit cannot survive that inspection. BitFire detects the malicious markup and blocks the request at the perimeter — the payload never reaches save_draft(), never lands in the draft store, and never loads into a victim's browser. This is behavior-based protection, not a CVE-specific virtual patch, so it also covers unreported variants of the same injection class. The WAF ships in BitFire FREE for eligible non-commercial sites; commercial sites run it under the appropriate commercial license.

The 2.10.2 Patch: Sanitization at Both Ends

Version 2.10.2 interrupts this chain at both ends. Server-side, the plugin promotes its existing Rich Text sanitizer into a shared Submission::sanitize_richtext_field() — a wp_kses element and attribute allow-list with protocol and style/class filtering plus a length cap — and routes every draft write through sanitize_draft_form_data() in save_draft() (including the update-in-place branch) and update_draft(); unconfigured string values fall back to sanitize_text_field(). Client-side, form.js replaces the raw innerHTML assignment with container._quill.clipboard.dangerouslyPasteHTML(value), filtering stored HTML through Quill's format-restricted clipboard parser as defense in depth. Update to 2.10.2 or later immediately — and remember that patching removes the flaw, not a compromise that already happened.

If Your Site Was Affected, Investigate for Persistence

A stored XSS in a form plugin is a foothold, not merely a bug. If your site ever ran 2.10.1 or earlier, treat it as potentially compromised: anyone persuaded to open a crafted resume link handed attacker script a session in your site origin. Patch first, then investigate. BitFire Threat Hunter performs a thorough post-compromise sweep for 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 it finds and rotate relevant credentials, including administrator passwords. An absence of obvious malicious files is not evidence your site is clean.

Protect Your Site With BitFire

Unauthenticated REST endpoints and unsanitized persistence should never meet, and CVE-2026-93303 shows what happens when they do. BitFire's WAF blocks the malicious form payload before WordPress runs, closing this attack class at the request layer without waiting for a plugin fix. Turn on BitFire today, update HT Contact Form to 2.10.2 or later, and run Threat Hunter if the vulnerable version was ever live. One blocked request is the difference between a normal day and an incident response.

03
Source review

Vulnerable and fixed code

The relevant source is located in admin/Includes/Api/Endpoints/Draft.php (save_draft/update_draft); DOM sink assets/js/form.js (_populateFormFields).

BeforeVulnerable behavior
// assets/js/form.js, _populateFormFields() — draft resume sink (2.10.1)
container._quill.root.innerHTML = value;
AfterCorrected behavior
// Corrected behavior, abridged from the supplied writeup (2.10.2, assets/js/form.js)
// draft-sourced HTML is routed through Quill's format-restricted clipboard
// parser and field.value is read back from the parsed editor DOM
container._quill.clipboard.dangerouslyPasteHTML(value);
field.value = container._quill.root.innerHTML;
04
Zero-day protection

Protection from the first exploit request

BitFire protects WordPress servers on day zero—before a vulnerability is publicly known and before other vendors have time to develop signatures or patches.

01 · VerifyStop unknown clients

Bot controls and browser verification stop untrusted automated clients before previously unknown exploit code reaches WordPress.

02 · DetectBlock malicious behavior

General WAF protections identify dangerous request behavior and hostile payloads without waiting for a vulnerability-specific signature.

03 · PreventContain attacks at runtime

RASP follows execution inside PHP and prevents unauthorized changes to protected files, accounts, and database content.

BitFire · WordPress protectionZero-day ready
BitFire zero-day WordPress vulnerability protection
BitFire combines verified-client controls, behavior-based WAF detection, and runtime RASP enforcement to protect WordPress before an exploit has a name, CVE, signature, or vendor patch.
About the author

Cory Marsh

Cory has more than 20 years of internet security experience and is a lead developer on the BitFire project.

Read BitFire security research →
Protect your WordPress website

Add protection before the next exploit arrives.

BitFire combines bot controls, request inspection, malware detection, and runtime protection in one WordPress security platform.

Protect my site free →