CVE-2026-93747 vulnerability and BitFire protection

How BitFire Blocks CVE-2026-93747: wpForo Forum Stored XSS

WordPress vulnerability research

BitFire's WAF blocks the wpForo CVE-2026-93747 stored XSS payload at the request layer, before it is stored or ever rendered to an admin.

Authenticated member account Medium severity (CVSS 6.4) Script execution in admin sessions Stored Cross-Site Scripting
BitFire · Vulnerability advisoryResearch published
AdvisoryCVE-2026-93747
ComponentwpForo Forum
Relevant sourceclasses/Forms.php — field_wrap_profile() (~line 322) and includes/functions-template.php — wpforo_apply_ucf_shortcode() (~line 2490)
Executive summary

What WordPress administrators need to know

wpForo Forum 3.1.6 and earlier let a logged-in member store unencoded markup in plain-text profile fields. The account-update handler only length-checks text input, and the profile renderer echoes the stored value raw — both in field_wrap_profile() and through the [wpfucf] template placeholder. The result is stored cross-site scripting that executes in the browser of anyone who views the profile, including site administrators. BitFire's WAF inspects the update request, detects the cross-site scripting payload, and blocks it before WordPress stores anything. Update to wpForo 3.1.7 for the vendor fix, and run BitFire Threat Hunter if an affected version was ever live on your site.

At a glance

Key facts

  • Vulnerable path: a nonce-verified, ownership-checked profile update (wpfaction=profile_update) persists text field values without HTML encoding.
  • Forms::prepare_values() encodes only whitelisted field types and names; built-in text fields such as 'occupation' render raw.
  • Both render sinks are affected: Forms::field_wrap_profile() and the [wpfucf] placeholder handler wpforo_apply_ucf_shortcode().
  • Injected script executes in the browser of any viewer allowed by the field's canView settings — administrators reviewing profiles included.
  • Fix in 3.1.7: new is_display_value_safe_html() whitelist plus conditional esc_html() at both sinks.
  • BitFire FREE WAF detects the script payload in the update POST and blocks storage; no PRO required for this request-layer control.
01
Vulnerability overview

Understand the exposure

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

Affected componentwpForo Forum
Potential reach20,000+ installations
Attack techniquestored cross-site scripting
Published2026-09-24
BitFire's WAF catches the wpForo profile-update payload at the request layer and blocks it before WordPress stores a byte — the injection never reaches your members' browsers.
02
Technical analysis

How the vulnerability works

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

Inside CVE-2026-93747: Stored XSS in Member Profile Fields

The storage step is an authenticated POST to the forum account page (wpfaction=profile_update). The form is nonce-verified and ownership is enforced, so the attacker needs only an ordinary member account — no administrator role. Forms::validate() applies no HTML encoding to text fields; it length-checks the value and even runs htmlspecialchars_decode(), so markup reaches the database untouched. On render, Forms::prepare_values() encodes only a short whitelist of field types and names. Everything else — including built-in text fields such as 'occupation' — is concatenated raw by field_wrap_profile(), and wpforo_apply_ucf_shortcode() returns stored values unescaped when filling [wpfucf] placeholders in HTML profile templates. Any visitor permitted to view the field executes the stored script in their own session, and administrators reviewing profiles are the highest-value targets.

BitFire FREE WAF Blocks the Payload Before It Is Stored

A stored XSS has exactly one delivery moment: the profile-update request. That POST body is exactly where BitFire intercepts it. The BitFire FREE Web Application Firewall inspects form fields and POST bodies before WordPress processes them and detects malicious JavaScript payloads in submitted data. When a profile update carries script content, BitFire blocks the request: the value never reaches the database, and no profile page ever renders it to an administrator. The one dependency — a matching script payload in inspected request data — is exactly what this exploit must transmit to function. Because the rules are behavior-based, they stop unknown CVEs without a CVE-specific virtual patch. The protection ships in BitFire FREE for eligible non-commercial websites; commercial sites need a commercial license.

If Your Site Was Affected, Investigate for Persistence

Updating to 3.1.7 closes the rendering hole, but it does not undo what an exploited session already did. Script that ran in an administrator's browser acted with that administrator's authority — new admin accounts, uploaded files, changed settings. If wpForo 3.1.6 or earlier was ever live on your site, run BitFire PRO Threat Hunter immediately. It performs a thorough post-compromise investigation and surfaces backdoor WordPress administrator accounts, hidden database triggers, long-running PHP processes, and droppers that can reinstall malware or reinfect a site that looks clean. Patch, investigate, remove every persistence mechanism it finds, and rotate administrator credentials. An absence of obvious malicious files is not proof your site is clean.

Patch Now, Then Let BitFire Hold the Line

Update wpForo to 3.1.7 today — the release also fixes an infinite recursion that caused fatal errors and 504 timeouts on forum pages. Then put BitFire in front of the site: the FREE WAF blocks cross-site scripting payloads in profile updates before they are stored, protection that holds even on the day a new bug ships. Already ran an affected version? Let BitFire PRO Threat Hunter sweep for backdoor admins, hidden triggers, and droppers. Get BitFire FREE now.

03
Source review

Vulnerable and fixed code

The relevant source is located in classes/Forms.php — field_wrap_profile() (~line 322) and includes/functions-template.php — wpforo_apply_ucf_shortcode() (~line 2490).

BeforeVulnerable behavior
// classes/Forms.php — field_wrap_profile() (wpForo 3.1.6)
$f = $this->prepare_values( $f, WPF()->current_object['userid'] );
// prepare_values() HTML-encodes only whitelisted types/names; plain text passes through untouched
$html .= $f['value'];

// includes/functions-template.php — wpforo_apply_ucf_shortcode() (3.1.6)
// substitutes the viewed member's stored value for [wpfucf field="..."] placeholders
return $f['value'];
AfterCorrected behavior
// Corrected behavior, abridged from the supplied writeup (wpForo 3.1.7)
// New whitelist helper in classes/Forms.php — true only for the field types and names
// prepare_values() already converts to trusted HTML (url, email, tel, file, avatar, color,
// textarea, tinymce, datetime, html; skype, location, signature, about)
public function is_display_value_safe_html( $f ) { /* whitelist check */ }

// classes/Forms.php — field_wrap_profile()
$html .= ( is_scalar( $f['value'] ) && ! $this->is_display_value_safe_html( $f ) )
    ? esc_html( (string) $f['value'] ) : $f['value'];

// includes/functions-template.php — wpforo_apply_ucf_shortcode()
return ( is_scalar( $f['value'] ) && ! $this->is_display_value_safe_html( $f ) )
    ? esc_html( (string) $f['value'] ) : $f['value'];
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 →