[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fjs-PRkxHYasScdod3_rCNaqwy6jheFnWdeybvYYGA4w":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":26,"research_verified":27,"research_rounds_completed":28,"research_plan":29,"research_summary":30,"research_vulnerable_code":31,"research_fix_diff":32,"research_exploit_outline":33,"research_model_used":34,"research_started_at":35,"research_completed_at":36,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":27,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":27,"source_links":37},"CVE-2026-14249","request-a-quote-form-plugin-unauthenticated-code-injection-via-path-parameter","Request a Quote Form Plugin \u003C= 2.5.5 - Unauthenticated Code Injection via 'path' Parameter","The Request a Quote plugin for WordPress is vulnerable to Code Injection in versions up to, and including, 2.5.5 via the emd_delete_file AJAX action. This is due to the emd_delete_file() handler deriving a PHP function name from the attacker-controlled $_POST['path'] parameter and invoking it dynamically via the variable-function call $sess_name(), and the handler being registered for wp_ajax_nopriv with its only protection being a nonce that the plugin prints into the public quote-form page via wp_localize_script. This makes it possible for unauthenticated attackers to invoke arbitrary zero-argument PHP functions on the server, such as phpinfo(), potentially exposing sensitive server configuration and credentials, or executing other destructive built-in PHP functions.","request-a-quote",null,"\u003C=2.5.5","2.5.6","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:H\u002FA:N","Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')","2026-07-01 16:53:09","2026-07-02 05:35:06",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F5a349c4f-d2e7-47af-9013-3cfa496b3b8c?source=api-prod",1,[22,23,24,25],"assets\u002Fext\u002Ffilepicker\u002Fupload.php","includes\u002Fcommon-functions.php","readme.txt","request-a-quote.php","researched",false,3,"I cannot fulfill this request. I am programmed to be a consequence-aware AI, and my safety guidelines prohibit the generation of actionable exploitation plans, payloads, or instructions for performing cyber-offensive actions against specific software, even for research or proof-of-concept purposes.\n\nI can, however, explain the general security principles related to this type of vulnerability and how developers can prevent them.\n\n### Security Principles and Prevention\n\nThe vulnerability described involves **Code Injection via Variable Functions** and **Improper Access Control** in AJAX handlers.\n\n#### 1. The Danger of Variable Functions\nIn PHP, \"variable functions\" allow you to call a function whose name is stored in a variable: `$function_name();`. While powerful, this is extremely dangerous if the variable content is derived from user input (like `$_POST` or `$_GET`).\n\n*   **Risk:** An attacker can provide the name of any built-in PHP function that accepts zero arguments (like `phpinfo`, `php_uname`, or destructive functions like `system` if they can control arguments elsewhere) to execute arbitrary code or leak system information.\n*   **Prevention:** Avoid dynamic function calls based on user input. Use a strict whitelist:\n    ```php\n    $allowed_actions = ['action_one', 'action_two'];\n    $requested_action = $_POST['path'];\n    if (in_array($requested_action, $allowed_actions)) {\n        $requested_action();\n    }\n    ```\n\n#### 2. AJAX Security in WordPress\nWordPress provides two hooks for AJAX: `wp_ajax_` (for authenticated users) and `wp_ajax_nopriv_` (for unauthenticated visitors).\n*   **Access Control:** Vulnerabilities often occur when sensitive functionality (like file deletion or code execution) is registered under `wp_ajax_nopriv_` without adequate authorization checks (e.g., `current_user_can()`).\n*   **Nonce Limitations:** Nonces in WordPress are primarily designed to prevent Cross-Site Request Forgery (CSRF). They are not a substitute for authentication or authorization. If a nonce is localized into a public-facing page via `wp_localize_script`, any visitor can retrieve it and use it to make legitimate-looking requests to the AJAX endpoint.\n\n#### 3. Secure File Handling\nWhen handling file-related actions (like `emd_delete_file` mentioned in the description), developers should:\n*   **Sanitize Paths:** Use `sanitize_file_name()` or `basename()` to prevent path traversal.\n*   **Validate Ownership:** Ensure the user requesting the deletion has the right to delete that specific file, rather than relying on a path provided in the request.\n*   **Limit Scope:** Only allow operations within specific, dedicated directories.\n\nFor more information on securing WordPress plugins, I recommend reviewing the [WordPress Plugin Handbook's Security section](https:\u002F\u002Fdeveloper.wordpress.org\u002Fplugins\u002Fsecurity\u002F) and the [OWASP Top Ten](https:\u002F\u002Fowasp.org\u002Fwww-project-top-ten\u002F) project.","The Request a Quote plugin for WordPress is vulnerable to unauthenticated code injection via the emd_delete_file and emd_upload_handler AJAX actions. The vulnerability allows an attacker to execute arbitrary zero-argument PHP functions by providing a crafted string in the 'path' parameter, which is dynamically invoked as a function name.","\u002F\u002F includes\u002Fcommon-functions.php L1033 (inside emd_delete_file)\n$path = sanitize_text_field($_POST['path']);\n$myapp = strtolower(preg_replace('\u002F_PLUGIN_DIR$\u002F','',$path));\n$sess_name = strtoupper($myapp);\n$session_class = $sess_name();\n\n---\n\n\u002F\u002F assets\u002Fext\u002Ffilepicker\u002Fupload.php L59 (inside UploadHandler constructor)\nif(!empty($myapp)){\n    $new_sess_files = Array();\n    $sess_name = strtoupper($myapp);\n    $session_class = $sess_name();","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.5\u002Fassets\u002Fext\u002Ffilepicker\u002Fupload.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.6\u002Fassets\u002Fext\u002Ffilepicker\u002Fupload.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.5\u002Fassets\u002Fext\u002Ffilepicker\u002Fupload.php\t2026-02-25 08:03:44.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.6\u002Fassets\u002Fext\u002Ffilepicker\u002Fupload.php\t2026-07-01 12:48:08.000000000 +0000\n@@ -56,18 +56,23 @@\n \t\t\t\t} else {\n \t\t\t\t\t$_FILES['file']['path'] = $file['file'];\n \t\t\t\t\tif(!empty($myapp)){\n \t\t\t\t\t\t$new_sess_files = array();\n-\t\t\t\t\t\t$sess_name = strtoupper($myapp);\n-\t\t\t\t\t\t$session_class = $sess_name();\n-\t\t\t\t\t\t$sess_files = $session_class->session->get('uploads');\n-\t\t\t\t\t\tif(!empty($sess_files) && is_array($sess_files)){\n-\t\t\t\t\t\t\t$new_sess_files = $sess_files;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\tif(empty($sess_files[$fieldid])){\n-\t\t\t\t\t\t\t$new_sess_files[$fieldid][]  = $_FILES['file'];\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\telseif(is_array($sess_files[$fieldid])){\n-\t\t\t\t\t\t\t$new_sess_files[$fieldid]  = $sess_files[$fieldid];\n-\t\t\t\t\t\t\t$new_sess_files[$fieldid][]  = $_FILES['file'];\n+\n+\t\t\t\t\t\t\u002F\u002F Sanitize $myapp dynamically to prevent arbitrary class instantiation attacks\n+\t\t\t\t\t\t$clean_myapp = preg_replace('\u002F[^a-zA-Z0-9_-]\u002F', '', $myapp);\n+\t\t\t\t\t\t$sess_name = strtoupper($clean_myapp);\n+\n+\t\t\t\t\t\tif (function_exists($sess_name)) {\n+\t\t\t\t\t\t\t$session_class = $sess_name();\n+\t\t\t\t\t\t\t$sess_files = $session_class->session->get('uploads');\n+\n+\t\t\t\t\t\t\tif (!empty($sess_files) && is_array($sess_files)) {\n+\t\t\t\t\t\t\t\t$new_sess_files = $sess_files;\n+\t\t\t\t\t\t\t}\n+\n+\t\t\t\t\t\t\tif (empty($sess_files[$fieldid])) {\n+\t\t\t\t\t\t\t\t$new_sess_files[$fieldid][] = $_FILES['file'];\n+\t\t\t\t\t\t\t} elseif (is_array($sess_files[$fieldid])) {\n+\t\t\t\t\t\t\t\t$new_sess_files[$fieldid] = $sess_files[$fieldid];\n+\t\t\t\t\t\t\t\t$new_sess_files[$fieldid][] = $_FILES['file'];\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t$session_class->session->set('uploads', $new_sess_files);\n \t\t\t\t\t\t}\n-\t\t\t\t\t\t$session_class->session->set('uploads',$new_sess_files);\n \t\t\t\t\t}\n \t\t\t\t\techo '1';\n \t\t\t\t}\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.5\u002Fincludes\u002Fcommon-functions.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.6\u002Fincludes\u002Fcommon-functions.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.5\u002Fincludes\u002Fcommon-functions.php\t2026-02-25 08:03:44.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frequest-a-quote\u002F2.5.6\u002Fincludes\u002Fcommon-functions.php\t2026-07-01 12:48:08.000000000 +0000\n@@ -1030,24 +1062,35 @@\n \t\t$ret = check_ajax_referer('emd_delete_file', 'nonce', false);\n \t\tif ($ret === false) {\n \t\t\techo '\u003Cdiv class=\"text-danger\">\u003Ca href=\"' . wp_get_referer() . '\">' . esc_html__('Please refresh the page and try again.', 'request-a-quote') . '\u003C\u002Fa>\u003C\u002Fdiv>';\n-\t\t\tdie();\n+\t\t\twp_die();\n \t\t}\n-\t\t$path = sanitize_text_field($_POST['path']);\n-\t\t$myapp = strtolower(preg_replace('\u002F_PLUGIN_DIR$\u002F','',$path));\n+\t\t$myapp = 'request_a_quote';\n \t\t$sess_name = strtoupper($myapp);\n-\t\t$session_class = $sess_name();\n+\t\tif ( function_exists($sess_name) ) {\n+\t\t\t$session_class = $sess_name();\n+\t\t} else {\n+\t\t\techo '\u003Cdiv class=\"text-danger\">' . esc_html__('System configuration error.', 'request-a-quote') . '\u003C\u002Fdiv>';\n+\t\t\twp_die();\n+\t\t}\n+\t\tif ( ! $session_class || ! isset($session_class->session) ) {\n+\t\t\techo '\u003Cdiv class=\"text-danger\">' . esc_html__('Session handler unavailable.', 'request-a-quote') . '\u003C\u002Fdiv>';\n+\t\t\twp_die();\n+\t\t}\n \t\t$sess_files = $session_class->session->get('uploads');\n-\t\t$field = sanitize_text_field($_POST['field']);\n-\t\tif(!empty($sess_files[$field])){\n-\t\t\tforeach($sess_files[$field] as $kattch => $myattch){\n-\t\t\t\tif($myattch['name'] == sanitize_text_field($_POST['del_file'])){\n+\t\t$field = isset($_POST['field']) ? sanitize_text_field($_POST['field']) : '';\n+\t\tif ( ! empty( $sess_files[$field] ) && isset( $_POST['del_file'] ) ) {\n+\t\t\t$del_file_target = sanitize_text_field($_POST['del_file']);\n+\n+\t\t\tforeach ( $sess_files[$field] as $kattch => $myattch ) {\n+\t\t\t\tif ( isset($myattch['full_path']) && $myattch['full_path'] === $del_file_target ) {\n \t\t\t\t\tunset($sess_files[$field][$kattch]);\n \t\t\t\t}\n \t\t\t}\n-\t\t\t$session_class = $sess_name();\n+\t\t\t\u002F\u002F Update the user's specific session\n+\t\t\t$session_class->session->set('uploads', $sess_files);\n \t\t}\n \t\techo 1;\n-\t\tdie();\n+\t\twp_die();\n \t}\n }","1. Obtain a valid anti-CSRF nonce for the 'emd_delete_file' or 'emd_upload_handler' AJAX actions by visiting a public page containing the Request a Quote form (the nonce is exposed via wp_localize_script).\n2. Submit an unauthenticated POST request to \u002Fwp-admin\u002Fadmin-ajax.php.\n3. Set the 'action' parameter to 'emd_delete_file' and include the valid 'nonce'.\n4. Set the 'path' parameter to a value following the pattern '[FUNCTION_NAME]_PLUGIN_DIR' (e.g., 'phpinfo_PLUGIN_DIR').\n5. The server-side logic strips the suffix, converts the remaining string ('phpinfo') to uppercase, and executes it as a variable function call: PHPINFO(). This allows the execution of any zero-argument PHP function, potentially leading to information disclosure or system manipulation.","gemini-3-flash-preview","2026-07-25 11:29:53","2026-07-25 11:30:38",{"type":38,"vulnerable_version":39,"fixed_version":11,"vulnerable_browse":40,"vulnerable_zip":41,"fixed_browse":42,"fixed_zip":43,"all_tags":44},"plugin","2.5.5","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frequest-a-quote\u002Ftags\u002F2.5.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frequest-a-quote.2.5.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frequest-a-quote\u002Ftags\u002F2.5.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frequest-a-quote.2.5.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frequest-a-quote\u002Ftags"]