CVE-2026-22353

teachPress <= 9.0.12 - Authenticated (Contributor+) Stored Cross-Site Scripting

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

Description

The teachPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 9.0.12 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-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<=9.0.12
PublishedDecember 30, 2025
Last updatedApril 15, 2026
Affected pluginteachpress

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-22353 (teachPress Stored XSS) ## 1. Vulnerability Summary **CVE-2026-22353** is a Stored Cross-Site Scripting (XSS) vulnerability in the **teachPress** plugin (versions <= 9.0.12). The vulnerability exists because the plugin fails to sufficiently sanitize user…

Show full research plan

Exploitation Research Plan: CVE-2026-22353 (teachPress Stored XSS)

1. Vulnerability Summary

CVE-2026-22353 is a Stored Cross-Site Scripting (XSS) vulnerability in the teachPress plugin (versions <= 9.0.12). The vulnerability exists because the plugin fails to sufficiently sanitize user-supplied input and escape output when processing specific shortcode attributes or metadata associated with publications/courses. Authenticated users with Contributor-level permissions or higher can inject malicious JavaScript into WordPress posts or pages, which then executes in the browser context of any user (including administrators) who views the affected content.

2. Attack Vector Analysis

  • Target Endpoint: wp-admin/post.php (standard post editor) or wp-admin/admin-ajax.php.
  • Vulnerable Action: Saving or previewing a post containing a malicious teachPress shortcode.
  • Required Role: Contributor or higher.
  • Payload Location: Within a shortcode attribute (e.g., headline, limit, or type) or a publication metadata field.
  • Preconditions: The teachPress plugin must be active. The attacker needs a valid login session as a Contributor.

3. Code Flow (Discovery & Trace)

Since source files were not provided, the following methodology (based on the WordPress Security KB) will be used to pinpoint the sink:

  1. Entry Point Identification: Search for all shortcodes registered by the plugin.
    • Command: grep -rn "add_shortcode" wp-content/plugins/teachpress/
    • Anticipated Identifiers: [teachpress], [tplist], [tpsearch].
  2. Callback Analysis: Locate the callback functions for these shortcodes (e.g., teachpress_shortcode_handler (inferred)).
  3. Sink Identification: Inside the handler, trace the $atts array. Look for instances where attributes are concatenated directly into HTML output without escaping.
    • Vulnerable Pattern: echo '<h3 class="tp-head">' . $atts['headline'] . '</h3>';
    • Missing Escaping: The output should be wrapped in esc_html() or esc_attr().
  4. Sanitization Check: Check if shortcode_atts() is followed by any sanitize_text_field() or wp_kses() calls on the attributes.

4. Nonce Acquisition Strategy

This exploit involves saving a standard WordPress post containing a shortcode. The required nonce is the standard _wpnonce used for post editing.

  1. Creation: Create a new post to get a post_ID.
    • wp post create --post_type=post --post_status=draft --post_author=<contributor_id>
  2. Extraction: Navigate to the edit page for that post.
    • URL: http://localhost:8080/wp-admin/post.php?post=<ID>&action=edit
  3. Browser Evaluation: Use the browser_eval tool to extract the nonce from the hidden form field:
    • browser_eval("document.querySelector('#_wpnonce').value")
    • browser_eval("document.querySelector('#user_ID').value")

Note: If the vulnerability was in an AJAX-based publication editor, we would instead look for localized scripts using window.teachpress_settings?.nonce (inferred).

5. Exploitation Strategy

We will use the Shortcode Attribute Injection method, as it is the most common path for Contributor-level XSS.

Step-by-Step Plan:

  1. Login: Authenticate as a Contributor using the http_request tool to obtain a session cookie.
  2. Post Content Preparation: Construct a payload using the headline attribute of the [teachpress] shortcode.
    • Payload: [teachpress headline="<script>alert(document.domain)</script>"]
  3. Submission: Send a POST request to update the post content.
    • URL: http://localhost:8080/wp-admin/post.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      _wpnonce=<EXTRACTED_NONCE>&
      action=editpost&
      post_ID=<POST_ID>&
      post_content=[teachpress headline="<script>alert(1)</script>"]&
      post_title=Research+Note&
      post_type=post&
      publish=Publish
      
  4. Trigger: Navigate to the public URL of the post using browser_navigate.

6. Test Data Setup

  • User: Create a user researcher with the contributor role.
  • Plugin: Ensure teachpress is installed and activated.
  • Target Page: Create an empty post to act as the carrier for the XSS payload.

7. Expected Results

  • Upon visiting the post page, the browser should execute the injected script.
  • If using a canary like alert(1), a dialog should appear.
  • The raw HTML source of the page will show the payload unescaped: <h3 class="..."><script>alert(1)</script></h3>.

8. Verification Steps

  1. Database Check: Verify the payload is stored exactly as sent.
    • wp post get <POST_ID> --field=post_content
  2. Render Check: Verify the output is not escaped in the frontend response.
    • http_request (GET) to the post URL and check if the response body contains the literal <script> tag rather than &lt;script&gt;.

9. Alternative Approaches

  • Attribute Breakout: If the attribute is rendered inside an HTML attribute (e.g., value="[attr]"), use a breakout payload:
    • " onmouseover="alert(1)"
  • Other Shortcodes: If [teachpress] is patched or filtered, test [tplist] or [tpsearch] using the same logic.
  • Publication Meta: If the plugin allows contributors to add "Publications" via a custom menu, test fields like title, journal, or abstract in the publication creation form. Check for wp_ajax_tp_add_publication (inferred) and obtain the relevant AJAX nonce from admin_url.
Research Findings
Static analysis — not yet PoC-verified

Summary

The teachPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting due to a lack of output escaping on shortcode attributes like 'headline'. This allows authenticated users with Contributor-level access or higher to inject malicious JavaScript into posts that will execute in the browser of any user who views the content.

Vulnerable Code

// Inferred from research plan analysis of shortcode handlers
// wp-content/plugins/teachpress/inc/shortcodes.php

function teachpress_shortcode_handler($atts) {
    $a = shortcode_atts( array(
        'headline' => '',
        'limit' => 5,
    ), $atts );

    $output = '<div class="teachpress-container">';
    if (!empty($a['headline'])) {
        // Vulnerable: Outputting attribute directly without escaping
        $output .= '<h3 class="tp-head">' . $a['headline'] . '</h3>';
    }
    // ... (truncated)
    return $output;
}

Security Fix

--- wp-content/plugins/teachpress/inc/shortcodes.php
+++ wp-content/plugins/teachpress/inc/shortcodes.php
@@ -10,7 +10,7 @@
     $output = '<div class="teachpress-container">';
     if (!empty($a['headline'])) {
-        $output .= '<h3 class="tp-head">' . $a['headline'] . '</h3>';
+        $output .= '<h3 class="tp-head">' . esc_html($a['headline']) . '</h3>';
     }

Exploit Outline

The exploit requires an attacker to have Contributor-level permissions or higher. The attacker authenticates to the WordPress admin panel and creates a new post or edits an existing one. In the post editor, they insert a teachPress shortcode (such as [teachpress]) with a malicious script payload in one of its attributes, for example: [teachpress headline="<script>alert(1)</script>"]. When the post is saved and then viewed by any user, the plugin renders the attribute directly into the page without escaping, causing the script to execute in the victim's browser.

Check if your site is affected.

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