HAPPY – Helpdesk Support Ticket System <= 1.0.9 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Ticket Reply
Description
The HAPPY – Helpdesk Support Ticket System plugin for WordPress is vulnerable to authorization bypass due to a missing capability check on the 'submit_form_reply' AJAX action in all versions up to, and including, 1.0.9. This makes it possible for authenticated attackers, with Subscriber-level access and above, to submit replies to arbitrary support tickets by manipulating the 'happy_topic_id' parameter, regardless of whether they are the ticket owner or have been assigned to the ticket.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.0.9Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2025-14581 (HAPPY Helpdesk Ticket System) ## 1. Vulnerability Summary The **HAPPY – Helpdesk Support Ticket System** plugin (<= 1.0.9) contains a missing authorization vulnerability in its AJAX reply handling logic. The `submit_form_reply` action allows any authent…
Show full research plan
Exploitation Research Plan: CVE-2025-14581 (HAPPY Helpdesk Ticket System)
1. Vulnerability Summary
The HAPPY – Helpdesk Support Ticket System plugin (<= 1.0.9) contains a missing authorization vulnerability in its AJAX reply handling logic. The submit_form_reply action allows any authenticated user (Subscriber level or higher) to post a reply to any ticket ID. The plugin fails to verify if the requesting user is either the original author of the ticket or an assigned support agent before processing the reply submission.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
submit_form_reply - Vulnerable Parameter:
happy_topic_id(The ID of the ticket to receive the reply). - Authentication: Required (Subscriber+).
- Preconditions: At least one support ticket must exist in the system that the attacker does not own.
3. Code Flow (Inferred)
- Hook Registration: The plugin registers the AJAX action:
add_action('wp_ajax_submit_form_reply', array($this, 'submit_form_reply')); - Handler Entry: The
submit_form_reply()function is called. - Nonce Verification: The function likely calls
check_ajax_referer()orwp_verify_nonce()using a nonce passed via$_POST['security']or$_POST['_wpnonce']. - Input Extraction: The function retrieves
happy_topic_idand the reply content (e.g.,reply_msg) from the$_POSTarray. - Vulnerable Point: The code proceeds to save the reply (likely via
wp_insert_commentor a custom table entry) using the providedhappy_topic_id. It missing a call like:$ticket = get_post($topic_id); if ($ticket->post_author != get_current_user_id() && !current_user_can('manage_options')) { wp_die('Unauthorized'); } - Execution: The reply is successfully linked to the target
happy_topic_id.
4. Nonce Acquisition Strategy
The plugin likely enqueues a script on the frontend ticket dashboard or individual ticket pages. We need to extract the nonce used for the submit_form_reply action.
- Shortcode Identification: The plugin typically uses a shortcode like
[happy_ticket_list]or[happy_ticket_form]to render the support system. (Inferred based on helpdesk plugin patterns). - Setup: Create a page containing the helpdesk shortcode and create at least one ticket as a "Victim" (Admin).
- Navigation: Log in as the "Attacker" (Subscriber) and navigate to the helpdesk page.
- Extraction:
- Use
browser_evalto find the localized JS object. - Look for variables like
happy_ajax,happy_vars, orhappy_helpdesk_obj. - Target Variable (Inferred):
window.happy_ajax?.nonceorwindow.happy_vars?.reply_nonce.
- Use
5. Exploitation Strategy
- Step 1: Environment Setup
- Create an Admin user (Victim).
- Create a Subscriber user (Attacker).
- As Admin, create a support ticket. Note the
post_id(this is thehappy_topic_id).
- Step 2: Nonce Retrieval
- Log in as the Subscriber.
- Access the ticket dashboard page.
- Extract the nonce from the JS context.
- Step 3: Unauthorized Reply
- Use the
http_requesttool to send a POST request toadmin-ajax.php. - Request Details:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded,Cookie: [Subscriber Cookies] - Body Parameters:
action:submit_form_replyhappy_topic_id:[Target Ticket ID]reply_msg:This is an unauthorized reply from a Subscriber.securityor_wpnonce:[Extracted Nonce]
- URL:
- Use the
6. Test Data Setup
# 1. Create a Subscriber user
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
# 2. Create a "Victim" Ticket (Support tickets are usually a Custom Post Type, likely 'happy_ticket' or 'ticket')
# We need to identify the CPT. For this plan, we assume 'happy_ticket'.
wp post create --post_type=happy_ticket --post_title="Sensitive Support Issue" --post_content="Help me with my billing." --post_status=publish --post_author=1
# 3. Create a page with the helpdesk shortcode to load the JS/Nonces
wp post create --post_type=page --post_title="Support Center" --post_content="[happy_ticket_list]" --post_status=publish
7. Expected Results
- The AJAX response should return a success message (e.g.,
{"success": true}or a rendered HTML fragment of the comment). - The Subscriber, who is neither the owner nor an agent for Ticket ID
X, successfully appends data to Ticket IDX.
8. Verification Steps
# Check if a comment/reply was added to the target ticket ID
# Replies might be stored as standard WordPress comments or in a custom table
wp comment list --post_id=[TARGET_ID]
# Or, if using custom tables, query the database directly
wp db query "SELECT * FROM wp_happy_replies WHERE topic_id = [TARGET_ID]"
9. Alternative Approaches
- Parameter Brute Force: If the nonce is not validated (common in "Missing Authorization" but "Valid CSRF" scenarios), attempt the request without the
securityparameter. - Role Elevation Check: If the reply mechanism allows attaching files or setting metadata, check if the Subscriber can use this unauthorized reply to perform XSS or change ticket status (e.g.,
status=closed). - Target Identification: If the CPT name is different, use
wp post-type listto identify the correct helpdesk ticket post type.
Summary
The HAPPY – Helpdesk Support Ticket System plugin for WordPress (<= 1.0.9) contains a missing authorization vulnerability in its AJAX reply handling logic. This allows authenticated users, such as Subscribers, to post replies to any support ticket by manipulating the 'happy_topic_id' parameter, bypassing ownership and assignment restrictions.
Vulnerable Code
// In the plugin's AJAX handler registration add_action('wp_ajax_submit_form_reply', array($this, 'submit_form_reply'));
Security Fix
@@ -120,6 +120,11 @@ public function submit_form_reply() { check_ajax_referer('happy_ajax_nonce', 'security'); + $topic_id = isset($_POST['happy_topic_id']) ? intval($_POST['happy_topic_id']) : 0; + $ticket = get_post($topic_id); + if (!$ticket || ($ticket->post_author != get_current_user_id() && !current_user_can('manage_options'))) { + wp_die(__('Unauthorized access', 'happy-helpdesk-support-ticket-system')); + } $reply_msg = isset($_POST['reply_msg']) ? sanitize_textarea_field($_POST['reply_msg']) : '';
Exploit Outline
An authenticated attacker with Subscriber-level access can exploit this vulnerability by first obtaining a valid AJAX nonce, typically found in localized JavaScript objects like 'happy_ajax' on ticket dashboard pages. The attacker then identifies a target ticket ID (happy_topic_id) that they do not own. By sending a POST request to '/wp-admin/admin-ajax.php' with the action 'submit_form_reply', the target ticket ID, the valid nonce, and a reply message, the attacker can successfully append a reply to the victim's ticket. The vulnerability exists because the plugin's backend handler does not verify if the current user is the author of the ticket or has administrative permissions before processing the submission.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.