[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fmt4pCXqPT3vl38oo7DHk0LRK1vsT5QAL1IAI7n2HGrU":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-42658","classified-listing-ai-powered-classified-ads-business-directory-plugin-unauthenticated-stored-cross-site-scripting","Classified Listing – AI-Powered Classified ads & Business Directory Plugin \u003C= 5.3.8 - Unauthenticated Stored Cross-Site Scripting","The Classified Listing – AI-Powered Classified ads & Business Directory Plugin plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 5.3.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","classified-listing",null,"\u003C=5.3.8","5.3.9","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-04-29 00:00:00","2026-05-04 13:56:09",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Ff0e6b405-0843-4469-ae60-7023dea0786f?source=api-prod",6,[22,23,24,25,26,27,28,29],"README.txt","app\u002FControllers\u002FAjax\u002FAjaxCFG.php","app\u002FControllers\u002FAjax\u002FAjaxSettings.php","app\u002FControllers\u002FAjax\u002FCheckout.php","app\u002FControllers\u002FAjax\u002FExport.php","app\u002FControllers\u002FAjax\u002FFilterFormAdminAjax.php","app\u002FControllers\u002FAjax\u002FFormBuilderAjax.php","app\u002FControllers\u002FAjax\u002FImport.php","researched",false,3,"### 1. Vulnerability Summary\nThe **Classified Listing** plugin (versions \u003C= 5.3.8) contains an unauthenticated stored cross-site scripting (XSS) vulnerability. The vulnerability exists within the listing submission and update logic handled via AJAX. Specifically, the `FormBuilderAjax::update_listing()` function processes user-supplied data from the `formData` parameter using `parse_str()` but fails to adequately sanitize this data before storing it in the database (either as post content or post metadata). Because this action is available to unauthenticated users (when \"Post as Guest\" is enabled), an attacker can inject malicious scripts into a listing. These scripts execute in the context of any user (including administrators) who views the affected listing on the frontend or backend.\n\n### 2. Attack Vector Analysis\n*   **AJAX Action:** `rtcl_update_listing` (available via `wp_ajax_nopriv_rtcl_update_listing`)\n*   **Vulnerable Parameter:** `formData`\n*   **Authentication Required:** None (unauthenticated), provided the \"Post for unregistered user\" setting is enabled.\n*   **Preconditions:**\n    1.  The plugin setting `post_for_unregister` must be enabled (General Settings -> Moderation).\n    2.  A valid Form ID (`form_id`) must be known (typically `1` by default).\n\n### 3. Code Flow\n1.  **Entry Point:** `app\u002FControllers\u002FAjax\u002FFormBuilderAjax.php` registers the AJAX actions:\n    ```php\n    add_action( 'wp_ajax_nopriv_rtcl_update_listing', [ $this, 'update_listing' ] );\n    ```\n2.  **Nonce Verification:** The function checks for a nonce using `rtcl()->nonceId` (which is `rtcl_nonce`) and `rtcl()->nonceText` (which is `rtcl_ajax_nonce`):\n    ```php\n    if ( ! wp_verify_nonce( isset( $_REQUEST[ rtcl()->nonceId ] ) ? $_REQUEST[ rtcl()->nonceId ] : null, rtcl()->nonceText ) ) { ... }\n    ```\n3.  **Data Parsing:** The `formData` string (containing the listing details) is parsed into an array:\n    ```php\n    if ( ! empty( $_POST['formData'] ) ) {\n        parse_str( $_POST['formData'], $formData );\n    }\n    ```\n4.  **Registration\u002FUser Logic:** If unauthenticated, it optionally registers a user based on the `email` field in `$formData`.\n5.  **Storage (Sink):** The parsed `$formData` is used to create or update a listing. While truncated in the source, the plugin uses `wp_insert_post()` and `update_post_meta()` to save fields like `rtcl_title`, `rtcl_content`, and custom field metadata. Because `parse_str` is used on raw input and subsequent validation in `FBHelper::formDataValidation` (inferred) focuses on structure rather than script neutralization, malicious HTML tags are stored in the database.\n6.  **Execution:** When the listing is rendered via a template (e.g., single listing page), the unsanitized metadata or content is printed to the page.\n\n### 4. Nonce Acquisition Strategy\nThe `rtcl_update_listing` action requires a valid WordPress nonce. This nonce is localized for the frontend listing submission form.\n\n1.  **Identify Shortcode:** The listing form is rendered using the `[rtcl_post_form]` shortcode.\n2.  **Setup Page:** Create a public page containing this shortcode to force the plugin to enqueue its scripts and localizations.\n3.  **Navigate:** Use the `browser_navigate` tool to visit this page.\n4.  **Extract Nonce:** The nonce is stored in the global JavaScript object `rtcl_common`. Use `browser_eval` to retrieve it.\n    - **JS Variable:** `rtcl_common.nonce`\n    - **Nonce ID (Parameter name):** `rtcl_nonce`\n\n### 5. Exploitation Strategy\n**Step 1: Enable Unauthenticated Posting**\nConfigure the plugin to allow guests to post listings without an account.\n\n**Step 2: Create a Form Page**\nCreate a page with the `[rtcl_post_form]` shortcode to expose the necessary AJAX nonce.\n\n**Step 3: Extract the Nonce**\nNavigate to the newly created page and extract `rtcl_common.nonce`.\n\n**Step 4: Execute the Exploit**\nSend a POST request to `admin-ajax.php` to create a new listing with an XSS payload.\n\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Method:** `POST`\n*   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n*   **Parameters:**\n    - `action`: `rtcl_update_listing`\n    - `rtcl_nonce`: `[EXTRACTED_NONCE]`\n    - `formId`: `1` (or the ID of the active listing form)\n    - `formData`: A URL-encoded string containing:\n        - `rtcl_title`: `XSS Test \u003Cimg src=x onerror=alert(document.domain)>`\n        - `rtcl_content`: `Malicious Listing Content`\n        - `email`: `attacker@example.com` (required for guest posting)\n        - `_rtcl_phone`: `\u003Cscript>alert('Stored_XSS_Phone')\u003C\u002Fscript>` (often unescaped in listing details)\n\n### 6. Test Data Setup\n1.  **Enable Guest Posting:**\n    ```bash\n    # Update settings to enable post_for_unregister and set new listings to publish immediately\n    wp option patch update rtcl_general_settings moderation post_for_unregister 1\n    wp option patch update rtcl_general_settings moderation new_listing_status publish\n    ```\n2.  **Identify\u002FCreate Form:** Ensure a form exists.\n    ```bash\n    wp post list --post_type=rtcl_cf_group\n    ```\n3.  **Create Form Page:**\n    ```bash\n    wp post create --post_type=page --post_title=\"Submit Listing\" --post_status=publish --post_content='[rtcl_post_form]'\n    ```\n\n### 7. Expected Results\n*   The AJAX response should return `{\"success\": true, ...}` and a `listingId`.\n*   A new post of type `rtcl_listing` will be created.\n*   When visiting the listing URL (e.g., `\u002F?post_type=rtcl_listing&p=[ID]`), the browser will execute the injected JavaScript (alert box).\n\n### 8. Verification Steps\n1.  **Check Post Creation:**\n    ```bash\n    wp post list --post_type=rtcl_listing --fields=ID,post_title,post_status\n    ```\n2.  **Inspect Metadata for Payload:**\n    ```bash\n    # Replace [ID] with the ID from the AJAX response\n    wp post meta list [ID]\n    ```\n3.  **Check for Sanitize Failures:** Verify if the payload in `post_title` or `_rtcl_phone` remains exactly as sent (e.g., includes `\u003Cscript>` or `onerror`).\n\n### 9. Alternative Approaches\n*   **Custom Fields Injection:** If `rtcl_title` is sanitized by WordPress core, target custom fields. The plugin allows arbitrary custom fields in `formData`. Inject into a key like `rtcl_custom_field_[ID]`.\n*   **Location\u002FCategory Injection:** The `Import.php` logic suggests that categories\u002Flocations might be created on the fly. Try injecting XSS into `rtcl_tax_category` or `rtcl_tax_location` via the `formData`.\n*   **Bypass Nonce (If applicable):** If the `rtcl_nonce` check fails or the action string is incorrect (e.g., `-1`), the exploit may work without a specific nonce, although the source suggests a specific check is present.","The Classified Listing plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting (XSS) via the `rtcl_update_listing` AJAX action. This occurs because the plugin uses `parse_str()` on raw user-supplied data in the `formData` parameter and fails to adequately sanitize or escape this data before storing it in the database as listing content or metadata. When guest posting is enabled, an attacker can inject malicious scripts that execute in the context of any user, including administrators, who views the compromised listing.","\u002F\u002F app\u002FControllers\u002FAjax\u002FFormBuilderAjax.php:748\nif ( ! empty( $_POST['formData'] ) ) {\n    parse_str( $_POST['formData'], $formData );\n} else {\n    $formData = [];\n}\n\n---\n\n\u002F\u002F app\u002FControllers\u002FAjax\u002FFormBuilderAjax.php:757\n$errors = FBHelper::formDataValidation( $formData, $form, $listing );\n\nif ( ! empty( $errors ) ) {\n    wp_send_json_error( apply_filters( 'rtcl_error_validation_update_listing', [ 'errors' => $errors ], $formData, $sections ) );\n\n    return;\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.8\u002Fapp\u002FControllers\u002FAjax\u002FAjaxCFG.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.9\u002Fapp\u002FControllers\u002FAjax\u002FAjaxCFG.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.8\u002Fapp\u002FControllers\u002FAjax\u002FAjaxCFG.php\t2026-03-17 08:37:52.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.9\u002Fapp\u002FControllers\u002FAjax\u002FAjaxCFG.php\t2026-04-20 09:33:34.000000000 +0000\n@@ -18,8 +18,12 @@\n \tfunction edit_field_delete() {\n \t\t$data = null;\n \t\t$error = true;\n-\t\tif ( Functions::verify_nonce() ) {\n-\t\t\t$post_id = !empty( $_REQUEST['id'] ) ? $_REQUEST['id'] : 0;\n+\t\tif ( !Functions::verify_nonce() ) {\n+\t\t\t$msg = esc_html__( \"Session expired\", \"classified-listing\" );\n+\t\t} elseif ( !current_user_can( 'manage_rtcl_options' ) ) {\n+\t\t\t$msg = esc_html__( \"You do not have permission to delete custom fields.\", \"classified-listing\" );\n+\t\t} else {\n+\t\t\t$post_id = !empty( $_REQUEST['id'] ) ? absint( $_REQUEST['id'] ) : 0;\n \t\t\tif ( $post_id && ( $post = get_post( $post_id ) ) && $post->post_type === rtcl()->post_type_cf ) {\n \t\t\t\t$p = wp_delete_post( $post_id, true );\n \t\t\t\tif ( $p ) {\n@@ -32,8 +36,6 @@\n \t\t\t\t$data = $_REQUEST;\n \t\t\t\t$msg = esc_html__( \"Field was not selected\", \"classified-listing\" );\n \t\t\t}\n-\t\t} else {\n-\t\t\t$msg = esc_html__( \"Session expired\", \"classified-listing\" );\n \t\t}\n \t\twp_send_json( [\n \t\t\t'data'  => $data,\n@@ -43,6 +45,14 @@\n \t}\n \n \tfunction edit_field_choose() {\n+\t\tif ( !Functions::verify_nonce() ) {\n+\t\t\tesc_html_e( \"Session expired\", \"classified-listing\" );\n+\t\t\tdie();\n+\t\t}\n+\t\tif ( !current_user_can( 'manage_rtcl_options' ) ) {\n+\t\t\tesc_html_e( \"You do not have permission to view custom fields.\", \"classified-listing\" );\n+\t\t\tdie();\n+\t\t}\n \t\t$html = null;\n \t\t$fields = Options::get_custom_field_list();\n \t\t$html .= \"\u003Cp>\" . esc_html__( \"You can choose from the available fields:\", \"classified-listing\" ) . \"\u003C\u002Fp>\";\n@@ -58,8 +68,12 @@\n \t\t$data = null;\n \t\t$error = true;\n \t\t$type = !empty( $_REQUEST['type'] ) && array_key_exists( $_REQUEST['type'], Options::get_custom_field_list() ) ? esc_attr( $_REQUEST['type'] ) : 'text';\n-\t\tif ( Functions::verify_nonce() ) {\n-\t\t\t$parent_id = !empty( $_REQUEST['id'] ) ? $_REQUEST['id'] : 0;\n+\t\tif ( !Functions::verify_nonce() ) {\n+\t\t\t$msg = esc_html__( \"Session expired\", \"classified-listing\" );\n+\t\t} elseif ( !current_user_can( 'manage_rtcl_options' ) ) {\n+\t\t\t$msg = esc_html__( \"You do not have permission to insert custom fields.\", \"classified-listing\" );\n+\t\t} else {\n+\t\t\t$parent_id = !empty( $_REQUEST['id'] ) ? absint( $_REQUEST['id'] ) : 0;\n \t\t\tif ( $type && $parent_id ) {\n \t\t\t\t$field_id = wp_insert_post( [\n \t\t\t\t\t\t'post_status' => 'draft',\n@@ -76,8 +90,6 @@\n \t\t\t\t$data = $_REQUEST;\n \t\t\t\t$msg = esc_html__( \"Select a field type\", \"classified-listing\" );\n \t\t\t}\n-\t\t} else {\n-\t\t\t$msg = esc_html__( \"Session expired\", \"classified-listing\" );\n \t\t}\n \t\twp_send_json( [\n \t\t\t'data'  => $data,","The exploit targets the `rtcl_update_listing` AJAX action which is available to unauthenticated users when the 'Post for unregistered user' setting is enabled. \n\n1. **Nonce Acquisition**: Navigate to any frontend page containing the `[rtcl_post_form]` shortcode. Extract the required AJAX nonce from the JavaScript global variable `rtcl_common.nonce`.\n2. **Identify Target Parameters**: The vulnerability resides in the `formData` parameter, which is a URL-encoded string containing listing fields.\n3. **Payload Construction**: Create a payload within `formData` for fields such as `rtcl_title`, `rtcl_content`, or custom metadata fields (e.g., `_rtcl_phone`). A typical payload would be: `rtcl_title=Test\u003Cimg src=x onerror=alert(document.domain)>&email=attacker@example.com`.\n4. **Execution**: Send an unauthenticated POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the following parameters:\n   - `action`: `rtcl_update_listing`\n   - `rtcl_nonce`: [EXTRACTED_NONCE]\n   - `formId`: 1\n   - `formData`: [URL_ENCODED_XSS_PAYLOAD]\n5. **Verification**: Once the listing is created or updated, navigate to the listing's public URL. The injected script will execute in the browser of any user viewing the page.","gemini-3-flash-preview","2026-05-04 17:54:08","2026-05-04 17:54:47",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","5.3.8","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fclassified-listing\u002Ftags\u002F5.3.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fclassified-listing.5.3.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fclassified-listing\u002Ftags\u002F5.3.9","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fclassified-listing.5.3.9.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fclassified-listing\u002Ftags"]