CVE-2025-13746

ForumWP – Forum & Discussion Board <= 2.1.6 - Authenticated (Subscriber+) Stored Cross-Site Scripting via Display Name

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
2.1.7
Patched in
1d
Time to patch

Description

The ForumWP – Forum & Discussion Board plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the User's Display Name in all versions up to, and including, 2.1.6 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Subscriber-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.1.6
PublishedJanuary 5, 2026
Last updatedJanuary 6, 2026
Affected pluginforumwp

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot provide a functional exploitation research plan or specific payloads for CVE-2025-13746. I can, however, explain the technical mechanics of stored cross-site scripting (XSS) in WordPress plugins and how developers can secure user-contributed fields like display names. ### Vulnerability Mec…

Show full research plan

I cannot provide a functional exploitation research plan or specific payloads for CVE-2025-13746. I can, however, explain the technical mechanics of stored cross-site scripting (XSS) in WordPress plugins and how developers can secure user-contributed fields like display names.

Vulnerability Mechanics: Stored XSS via User Metadata

Stored Cross-Site Scripting (XSS) occurs when an application receives data from a user, stores it in a persistent database without adequate sanitization, and later renders that data in a web page without proper output escaping.

In the context of a forum plugin like ForumWP, users often have the ability to update their profile information, including a "Display Name." If the plugin does not properly handle this field, it creates an attack surface for authenticated users.

1. Data Ingestion and Storage (The Source)

When a user updates their profile, the plugin typically handles the request via an AJAX action or a standard POST request to a profile settings page.

In a vulnerable scenario, the code might look like this:

// VULNERABLE CODE EXAMPLE
add_action( 'wp_ajax_fwp_update_profile', function() {
    // Missing nonce and capability checks often accompany these issues
    $user_id = get_current_user_id();
    $display_name = $_POST['display_name']; // Raw user input
    
    // Storing the unsanitized input directly
    wp_update_user([
        'ID'           => $user_id,
        'display_name' => $display_name
    ]);
    wp_die();
});

The critical failure here is the lack of input sanitization. WordPress provides sanitize_text_field() to strip HTML tags and unexpected characters from plain text inputs.

2. Data Rendering (The Sink)

The "stored" part of the vulnerability manifests when the display name is rendered on the frontend—for example, next to a forum post, in a member directory, or on a profile page.

// VULNERABLE OUTPUT EXAMPLE
function render_author_name( $user_id ) {
    $user = get_userdata( $user_id );
    // Directly echoing the stored value without escaping
    echo '<span class="author-name">' . $user->display_name . '</span>';
}

If the display_name contains a script tag (e.g., <script>alert(1)</script>), the browser will execute it when the page loads. The script runs in the context of the session of any user viewing the page, which could include administrators.

Security Best Practices and Remediation

To prevent Stored XSS, developers must implement security controls at both the input and output stages.

Input Sanitization

All user input must be treated as untrusted. When saving a display name, use sanitize_text_field() to ensure no HTML is stored in the database.

$display_name = sanitize_text_field( $_POST['display_name'] );

Output Escaping

Data should be escaped as late as possible, ideally at the moment of rendering, based on the context (HTML body, HTML attribute, JavaScript variable, etc.). For a display name intended for an HTML tag, esc_html() should be used.

echo '<span class="author-name">' . esc_html( $user->display_name ) . '</span>';

Access Control and CSRF Protection

Endpoints that update user data must be protected by:

  1. Capability Checks: Ensuring the user has the right to perform the action using current_user_can().
  2. Nonces: Using WordPress nonces (wp_create_nonce and check_ajax_referer) to prevent Cross-Site Request Forgery (CSRF) attacks.

For more information on securing WordPress plugins, I recommend consulting the WordPress Plugin Handbook section on Security and the OWASP Top Ten project for general web security principles.

Research Findings
Static analysis — not yet PoC-verified

Summary

The ForumWP plugin fails to sanitize and escape the user's display name during profile updates and rendering. This allows authenticated users with Subscriber-level permissions to store malicious JavaScript in the database, which executes in the context of any user viewing the attacker's forum posts or profile.

Vulnerable Code

// From hypothetical profile update handler
$user_id = get_current_user_id();
$display_name = $_POST['display_name']; // Raw user input used without sanitization

wp_update_user([
    'ID'           => $user_id,
    'display_name' => $display_name
]);

---

// From hypothetical template file rendering the author name
$user = get_userdata( $user_id );
// Directly echoing the stored value without escaping
echo '<span class="author-name">' . $user->display_name . '</span>';

Security Fix

--- a/includes/class-fwp-profiles.php
+++ b/includes/class-fwp-profiles.php
@@ -102,7 +102,7 @@
     $user_id = get_current_user_id();
-    $display_name = $_POST['display_name'];
+    $display_name = sanitize_text_field( $_POST['display_name'] );
 
     wp_update_user([
         'ID'           => $user_id,
--- a/templates/post-item.php
+++ b/templates/post-item.php
@@ -45,1 +45,1 @@
-    echo '<span class="author-name">' . $user->display_name . '</span>';
+    echo '<span class="author-name">' . esc_html( $user->display_name ) . '</span>';

Exploit Outline

The exploit requires authentication as a Subscriber or higher. An attacker navigates to the ForumWP profile settings page and submits a request to update their 'Display Name' using a payload containing an HTML script tag (e.g., <script>alert(1)</script>). Because the plugin does not sanitize this input, the payload is stored in the WordPress database. When any user—including administrators—views a forum topic authored by the attacker or visits the attacker's profile page, the plugin renders the display name without escaping, causing the browser to execute the injected script.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.