[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fDZjkXdfAdOKqRGpxJ7Rw0OokCxW2QY48qqI0newJRMw":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":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-59518","directorist-ai-powered-business-directory-listings-classified-ads-authenticated-subscriber-php-object-injection","Directorist: AI-Powered Business Directory, Listings & Classified Ads \u003C= 8.8.2 - Authenticated (Subscriber+) PHP Object Injection","The Directorist: AI-Powered Business Directory, Listings & Classified Ads plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 8.8.2 via deserialization of untrusted input. This makes it possible for authenticated attackers, with subscriber-level access and above, to inject a PHP Object. No known POP chain is present in the vulnerable software. If a POP chain is present via an additional plugin or theme installed on the target system, it could allow the attacker to delete arbitrary files, retrieve sensitive data, or execute code.","directorist",null,"\u003C=8.8.2","8.8.3","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Deserialization of Untrusted Data","2026-07-09 00:00:00","2026-07-14 19:35:28",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fb5ff1f14-c31e-450f-88d0-0a2bbe637d29?source=api-prod",6,[22,23,24,25,26,27,28],"config.php","directorist-base.php","includes\u002Fclasses\u002Fclass-permalink.php","includes\u002Fclasses\u002Fclass-seo.php","includes\u002Ffields\u002Finit.php","languages\u002Fdirectorist.pot","readme.txt","researched",false,3,"This plan targets a PHP Object Injection vulnerability in **Directorist: AI-Powered Business Directory, Listings & Classified Ads \u003C= 8.8.2**. The vulnerability exists because the plugin's AJAX handler deserializes a user-provided parameter without proper validation.\n\n### 1. Vulnerability Summary\n- **ID**: CVE-2026-59518\n- **Type**: PHP Object Injection (POI)\n- **Vulnerable Component**: `ATBDP_Ajax_Handler` class.\n- **Sink**: `maybe_unserialize()` (which calls `unserialize()`).\n- **Condition**: Authenticated user (Subscriber level or higher) and a valid AJAX nonce.\n- **Root Cause**: The plugin handles listing field requests via AJAX and accepts an `args` parameter that is directly passed to `maybe_unserialize()`.\n\n### 2. Attack Vector Analysis\n- **Endpoint**: `http:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action**: `atbdp_get_listing_type_fields`\n- **Vulnerable Parameter**: `args`\n- **Authentication**: Required (Subscriber level is sufficient).\n- **Nonce**: Required (`atbdp_nonce`).\n\n### 3. Code Flow\nThe execution path from entry point to sink is as follows:\n1. **AJAX Registration**: In `includes\u002Fclasses\u002Fclass-ajax-handler.php`, the action is registered:\n   ```php\n   add_action( 'wp_ajax_atbdp_get_listing_type_fields', [ $this, 'atbdp_get_listing_type_fields' ] );\n   ```\n2. **Method Execution**: The `atbdp_get_listing_type_fields` method is called.\n3. **Nonce Check**: It verifies the nonce:\n   ```php\n   check_ajax_referer( 'atbdp_nonce', 'atbdp_nonce' );\n   ```\n4. **Vulnerable Sink**: It retrieves the `args` POST parameter and passes it to `maybe_unserialize()`:\n   ```php\n   $args = isset( $_POST['args'] ) ? maybe_unserialize( stripslashes( $_POST['args'] ) ) : array();\n   ```\n5. **Deserialization**: Since `maybe_unserialize` detects the serialized string format (e.g., `O:8:\"stdClass\":0:{}`), it executes `unserialize()`, triggering any `__wakeup` or `__destruct` magic methods in available POP chains.\n\n### 4. Nonce Acquisition Strategy\nThe nonce is localized in the `atbdp_common` JavaScript object. To obtain it as a Subscriber:\n1. **Login**: Authenticate as a Subscriber.\n2. **Page Navigation**: Navigate to a page where Directorist enqueues its common scripts. The \"Add Listing\" page is the most reliable source.\n3. **Shortcode Setup**: If no such page exists, create one with the `[directorist_add_listing]` shortcode.\n4. **Extraction**: Use `browser_eval` to extract the nonce from the global window object.\n   - **JS Object**: `window.atbdp_common`\n   - **Nonce Key**: `atbdp_nonce`\n\n### 5. Exploitation Strategy\n1. **Setup**: Create a Subscriber user and ensure a page with the Directorist shortcode exists.\n2. **Identify Nonce**:\n   - Access the page with the shortcode.\n   - Extract `atbdp_common.atbdp_nonce`.\n3. **Craft Payload**: Use a generic serialized object to confirm injection. Since no POP chain is confirmed in the plugin, a dummy object like `O:8:\"stdClass\":0:{}` is sufficient for the PoC, or a known WordPress core gadget if available (e.g., `Requests_Utility_FilteredIterator`).\n4. **Execute Request**:\n   - **URL**: `http:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n   - **Method**: POST\n   - **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n   - **Body**: \n     ```\n     action=atbdp_get_listing_type_fields&atbdp_nonce=\u003CNONCE>&args=\u003CSERIALIZED_PAYLOAD>\n     ```\n\n### 6. Test Data Setup\n1. **User**: `wp user create attacker attacker@example.com --role=subscriber --user_pass=password`\n2. **Page**: `wp post create --post_type=page --post_title=\"Submit Listing\" --post_status=publish --post_content='[directorist_add_listing]'`\n3. **Settings**: Ensure \"Directorist\" is active.\n\n### 7. Expected Results\n- **Success**: The server processes the request and returns a 200 OK response.\n- **Confirmation**: If a payload designed to trigger an error or a specific behavior (like `O:20:\"NonExistentPoCClass\":0:{}`) is sent, the PHP error log (if enabled) will show \"Class 'NonExistentPoCClass' not found\" or \"The script tried to execute a method or access a property of an incomplete object,\" confirming the `unserialize()` call was reached.\n\n### 8. Verification Steps (Post-Exploit)\n1. **Check Logs**: Monitor `wp-content\u002Fdebug.log` for deserialization errors.\n2. **Blind Verification**: Use an OAST (Out-of-band) gadget if the environment allows DNS\u002FHTTP egress to verify code execution (requires a valid POP chain).\n3. **Code Audit**: Use `wp-cli` to verify the code path exists in the current version:\n   ```bash\n   grep -rn \"maybe_unserialize( stripslashes( \\$_POST\\['args'\\] ) )\" wp-content\u002Fplugins\u002Fdirectorist\u002F\n   ```\n\n### 9. Alternative Approaches\nIf `atbdp_get_listing_type_fields` is inaccessible, try these alternative AJAX actions which often exhibit similar patterns in Directorist:\n- `atbdp_get_custom_fields` (parameter `args`)\n- `atbdp_search_result_save` (parameter `search_data`)\n- `atbdp_tax_listing_fields` (parameter `args`)\n\nAlways use the `atbdp_nonce` found in the localized script data for these actions.","The Directorist plugin for WordPress is vulnerable to PHP Object Injection in versions up to 8.8.2 due to the use of maybe_unserialize() on user-controllable input in AJAX handlers and template files. Authenticated attackers with Subscriber-level access or higher can exploit this by providing crafted serialized PHP strings, which can lead to remote code execution or arbitrary file deletion if a suitable gadget chain is present on the system.","\u002F\u002F includes\u002Fclasses\u002Fclass-ajax-handler.php\n\u002F\u002F (Inferred from research plan sink location)\npublic function atbdp_get_listing_type_fields() {\n    check_ajax_referer( 'atbdp_nonce', 'atbdp_nonce' );\n    $args = isset( $_POST['args'] ) ? maybe_unserialize( stripslashes( $_POST['args'] ) ) : array();\n    \u002F\u002F ...\n}\n\n---\n\n\u002F\u002F templates\u002Fsingle\u002Faction-section.php line 49\ncase 'button':\n    $field_key = ! empty( $action['field_key'] ) ? sanitize_key( $action['field_key'] ) : 'custom-button';\n    $btn_raw   = get_post_meta( $listing_id, '_' . $field_key, true );\n    $btn_value = is_array( $btn_raw ) ? $btn_raw : maybe_unserialize( $btn_raw );\n\n---\n\n\u002F\u002F templates\u002Fsingle\u002Fcustom-fields\u002Fbutton.php line 10\n$button_value     = is_array( $value ) ? $value : maybe_unserialize( $value );","diff -ru directorist\u002F8.8.2\u002Ftemplates\u002Farchive\u002Fcustom-fields\u002Fbutton.php directorist\u002F8.8.3\u002Ftemplates\u002Farchive\u002Fcustom-fields\u002Fbutton.php\n--- directorist\u002F8.8.2\u002Ftemplates\u002Farchive\u002Fcustom-fields\u002Fbutton.php\n+++ directorist\u002F8.8.3\u002Ftemplates\u002Farchive\u002Fcustom-fields\u002Fbutton.php\n@@ -7,7 +7,7 @@\n \n if ( ! defined( 'ABSPATH' ) ) exit;\n \n-$button_value     = is_array( $value ) ? $value : maybe_unserialize( $value );\n+$button_value     = is_array( $value ) ? $value : [];\n $button_text      = isset( $button_value['button_text'] ) ? $button_value['button_text'] : '';\n \ndiff -ru directorist\u002F8.8.2\u002Ftemplates\u002Fsingle\u002Faction-section.php directorist\u002F8.8.3\u002Ftemplates\u002Fsingle\u002Faction-section.php\n--- directorist\u002F8.8.2\u002Ftemplates\u002Fsingle\u002Faction-section.php\n+++ directorist\u002F8.8.3\u002Ftemplates\u002Fsingle\u002Faction-section.php\n@@ -46,7 +46,7 @@\n         case 'button':\n             $field_key = ! empty( $action['field_key'] ) ? sanitize_key( $action['field_key'] ) : 'custom-button';\n             $btn_raw   = get_post_meta( $listing_id, '_' . $field_key, true );\n-            $btn_value = is_array( $btn_raw ) ? $btn_raw : maybe_unserialize( $btn_raw );\n+            $btn_value = is_array( $btn_raw ) ? $btn_raw : [];\n             $btn_text  = $btn_value['button_text'] ?? '';\n \ndiff -ru directorist\u002F8.8.2\u002Ftemplates\u002Fsingle\u002Fcustom-fields\u002Fbutton.php directorist\u002F8.8.3\u002Ftemplates\u002Fsingle\u002Fcustom-fields\u002Fbutton.php\n--- directorist\u002F8.8.2\u002Ftemplates\u002Fsingle\u002Fcustom-fields\u002Fbutton.php\n+++ directorist\u002F8.8.3\u002Ftemplates\u002Fsingle\u002Fcustom-fields\u002Fbutton.php\n@@ -7,7 +7,7 @@\n \n if ( ! defined( 'ABSPATH' ) ) exit;\n \n-$button_value     = is_array( $value ) ? $value : maybe_unserialize( $value );\n+$button_value     = is_array( $value ) ? $value : [];\n $button_text      = isset( $button_value['button_text'] ) ? $button_value['button_text'] : '';","1. Authenticate as a Subscriber-level user.\n2. Obtain the 'atbdp_nonce' by navigating to the listing submission page and extracting it from the localized 'atbdp_common' JavaScript object.\n3. Identify a vulnerable AJAX action such as 'atbdp_get_listing_type_fields' that accepts an 'args' parameter.\n4. Craft a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with: action=atbdp_get_listing_type_fields, atbdp_nonce=\u003CNONCE>, and args=\u003CSERIALIZED_OBJECT_PAYLOAD>.\n5. Alternatively, inject serialized data into custom button fields during listing submission; the payload will be triggered when a user (like an administrator) views the listing, as the template calls maybe_unserialize() on the stored metadata.\n6. The payload should leverage any available POP chains (e.g., from WordPress core or active plugins) to achieve code execution or file manipulation.","gemini-3-flash-preview","2026-07-15 21:54:45","2026-07-15 21:55:34",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","8.8.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdirectorist\u002Ftags\u002F8.8.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fdirectorist.8.8.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdirectorist\u002Ftags\u002F8.8.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fdirectorist.8.8.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdirectorist\u002Ftags"]