[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f9e3GpsZ_-0lO_aKcuTayqncc7RhjVkhaJfS6f6Ch5c0":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-4006","draft-list-authenticated-contributor-stored-cross-site-scripting-via-displayname-parameter","Draft List \u003C= 2.6.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'display_name' Parameter","The Simple Draft List plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'display_name' post meta (Custom Field) in all versions up to and including 2.6.2. This is due to insufficient input sanitization and output escaping on the author display name when no author URL is present. The plugin accesses `$draft_data->display_name` which, because `display_name` is not a native WP_Post property, triggers WP_Post::__get() and resolves to `get_post_meta($post_id, 'display_name', true)`. When the `user_url` meta field is empty, the `$author` value is assigned to `$author_link` on line 383 without any escaping (unlike line 378 which uses `esc_html()` for the `{{author}}` tag, and line 381 which uses `esc_html()` when a URL is present). This unescaped value is then inserted into the shortcode output via `str_replace()`. 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 a page containing the `[drafts]` shortcode with the `{{author+link}}` template tag.","simple-draft-list",null,"\u003C=2.6.2","2.6.3","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-18 18:13:45","2026-03-19 06:46:15",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fb5f0dc1a-6b6a-4370-a368-3687cffb43fc?source=api-prod",1,[22,23,24],"inc\u002Fcreate-lists.php","readme.txt","simple-draft-list.php","researched",false,3,"# Research Plan: CVE-2026-4006 - Stored XSS via `display_name` Post Meta\n\n## 1. Vulnerability Summary\nThe **Draft List** plugin (\u003C= 2.6.2) is vulnerable to Stored Cross-Site Scripting (XSS) due to insufficient output escaping in its shortcode rendering logic. Specifically, the plugin attempts to display an author's name using the `{{author+link}}` template tag. When processing this tag, the plugin accesses a property `$draft_data->display_name`. Because `WP_Post` objects do not natively have a `display_name` property, WordPress's magic `__get()` method fetches the value from the post meta table (`get_post_meta($post_id, 'display_name', true)`). \n\nAn authenticated attacker with Contributor-level permissions can create a draft and set a custom field named `display_name` containing a malicious script. When a user (including an Administrator) views a page containing the `[drafts]` shortcode with the `{{author+link}}` template tag, the script executes because the plugin fails to escape the meta-sourced value when no author URL is associated with it.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `wp-admin\u002Fpost.php` (to store the payload) and any frontend page containing the `[drafts]` shortcode (to trigger the payload).\n- **Vulnerable Parameter**: `meta_input[display_name]` (Custom Field\u002FPost Meta).\n- **Authentication Level**: Authenticated Contributor or higher. Contributors can create posts and manage their own post meta.\n- **Preconditions**: \n    1. The attacker must be able to create or edit a post (Contributor role).\n    2. A page or widget must exist that uses the `[drafts]` shortcode.\n    3. The shortcode must use a template containing the `{{author+link}}` tag.\n\n## 3. Code Flow\n1. **Entry Point**: A user views a page with the shortcode `[drafts]`.\n2. **Shortcode Handling**: `draft_list_shortcode()` in `inc\u002Fcreate-lists.php` is called.\n3. **List Generation**: `draft_list_shortcode()` calls `draft_list_generate_code()`.\n4. **Data Retrieval**: `draft_list_generate_code()` fetches draft posts. Each post is returned as a `WP_Post` object (stored in `$draft_data`).\n5. **Template Processing**: The code iterates through the template strings.\n6. **Property Access**: The code accesses `$draft_data->display_name`.\n7. **Magic Getter**: `WP_Post::__get('display_name')` executes `get_post_meta($post_id, 'display_name', true)`.\n8. **The Sink**: In `inc\u002Fcreate-lists.php` (around line 383), if the author's URL is empty, the variable `$author_link` is assigned the value of `$author` (which holds the meta value) without passing through `esc_html()` or `esc_attr()`.\n9. **Rendering**: The unescaped `$author_link` is inserted into the final HTML output via `str_replace()` and returned to the browser.\n\n## 4. Nonce Acquisition Strategy\n### Storing the Payload (Post Meta)\nTo store the malicious payload as a Contributor, the attacker needs to update a post's meta. This is typically done via the `wp-admin\u002Fpost.php` endpoint.\n1. **Action**: `editpost`\n2. **Nonce**: The `_wpnonce` is required for authorized post updates.\n3. **Acquisition**:\n    - Use `browser_navigate` to go to `wp-admin\u002Fpost-new.php`.\n    - Use `browser_eval` to extract the nonce from the document: \n      `browser_eval(\"document.querySelector('#_wpnonce').value\")`.\n\n### Triggering the Payload (Frontend)\nNo nonce is required to view the frontend output of a shortcode.\n\n## 5. Exploitation Strategy\n### Step 1: Create a Draft Post and Inject Payload\n1. Log in as a **Contributor**.\n2. Navigate to `wp-admin\u002Fpost-new.php`.\n3. Extract the `_wpnonce` and the newly generated `post_ID`.\n4. Send a POST request to `wp-admin\u002Fpost.php` to save the malicious meta:\n    - **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fpost.php`\n    - **Method**: `POST`\n    - **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n    - **Body**:\n      ```text\n      action=editpost\n      post_ID=[ID]\n      _wpnonce=[NONCE]\n      post_title=XSS-Draft\n      post_type=post\n      post_status=draft\n      meta_input[display_name]=\u003Cimg src=x onerror=alert(\"CVE-2026-4006\")>\n      ```\n\n### Step 2: Setup the Trigger Page\n1. Log in as an **Administrator**.\n2. Create a public page that renders the draft list with the vulnerable template tag.\n    - **Action**: `wp post create --post_type=page --post_title=\"Draft List Test\" --post_status=publish --post_content='[drafts template=\"{{author+link}}{{draft}}\"]'`\n\n### Step 3: Trigger Execution\n1. Navigate to the newly created \"Draft List Test\" page.\n2. The browser will render the list of drafts.\n3. When it reaches the attacker's draft, it will fetch the `display_name` meta and inject the `\u003Cimg ...>` tag into the DOM.\n\n## 6. Test Data Setup\n- **Users**: \n    - `contributor_user`: Role `contributor`\n    - `admin_user`: Role `administrator`\n- **Posts**:\n    - One draft post created by `contributor_user` with meta key `display_name` set to `\u003Cimg src=x onerror=alert(document.domain)>`.\n- **Shortcode Page**:\n    - A page containing `[drafts template=\"{{author+link}}{{draft}}\"]`.\n\n## 7. Expected Results\n- The HTTP response from the frontend page will contain the literal, unescaped string: `\u003Cimg src=x onerror=alert(\"CVE-2026-4006\")>`.\n- In a browser context, the JavaScript `alert` will execute.\n\n## 8. Verification Steps\n1. **Verify Meta Storage**:\n   `wp post meta get [POST_ID] display_name`\n   - Should return the XSS payload.\n2. **Verify Unescaped Output**:\n   Use `http_request` to fetch the frontend page and grep for the payload.\n   ```bash\n   grep '\u003Cimg src=x onerror=alert(\"CVE-2026-4006\")>' \n   ```\n3. **Verify Context**:\n   Confirm that the `{{author+link}}` tag was replaced by the payload without any HTML entity encoding (e.g., no `&lt;`).\n\n## 9. Alternative Approaches\n- **Template Parameter Abuse**: If the attacker cannot edit a page to add the shortcode, they can attempt to find a widget or an existing page that uses `[drafts]` and rely on the fact that any post they create (as a draft) will be pulled into that list automatically if it meets the shortcode's criteria (like `limit` or `type`).\n- **Shortcode Injection**: If the site allows Contributors to use `unfiltered_html` (rare) or if there is another way to place shortcodes, the attacker can provide the `template` attribute directly in the shortcode: `[drafts template=\"{{author+link}}\"]`.\n- **Author URL bypass**: If the vulnerability logic requires the `user_url` to be empty, ensure that the Contributor user profile has an empty Website field in their WordPress profile, OR verify that the plugin is looking for a `user_url` post meta (which will be empty by default for a new post). The vulnerability description specifically mentions `$author_link` is assigned `$author` on line 383 when the URL is empty.","The Draft List plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'display_name' post meta in versions up to 2.6.2. Authenticated contributors can inject malicious scripts into the 'display_name' custom field, which are then executed when a user views a page containing the [drafts] shortcode with the {{author+link}} template tag because the plugin fails to escape the meta-derived value when no author URL is present.","\u002F\u002F inc\u002Fcreate-lists.php line 380\n\n\t\t\t\t\tif ( '' !== $author_url ) {\n\t\t\t\t\t\t$author_link = '\u003Ca href=\"' . esc_url( $author_url ) . '\">' . esc_html( $author ) . '\u003C\u002Fa>';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$author_link = $author;\n\t\t\t\t\t}\n\t\t\t\t\t$this_line = str_replace( '{{author+link}}', $author_link, $this_line );","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsimple-draft-list\u002F2.6.2\u002Finc\u002Fcreate-lists.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsimple-draft-list\u002F2.6.3\u002Finc\u002Fcreate-lists.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsimple-draft-list\u002F2.6.2\u002Finc\u002Fcreate-lists.php\t2025-10-10 16:00:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsimple-draft-list\u002F2.6.3\u002Finc\u002Fcreate-lists.php\t2026-03-15 08:00:42.000000000 +0000\n@@ -380,7 +380,7 @@\n \t\t\t\t\tif ( '' !== $author_url ) {\n \t\t\t\t\t\t$author_link = '\u003Ca href=\"' . esc_url( $author_url ) . '\">' . esc_html( $author ) . '\u003C\u002Fa>';\n \t\t\t\t\t} else {\n-\t\t\t\t\t\t$author_link = $author;\n+\t\t\t\t\t\t$author_link = esc_html( $author );\n \t\t\t\t\t}\n \t\t\t\t\t$this_line = str_replace( '{{author+link}}', $author_link, $this_line );","1. Log in as a Contributor or higher level user.\n2. Create or edit a draft post.\n3. Add a Post Meta (Custom Field) entry with the key 'display_name' and a value containing a malicious script, such as: \u003Cimg src=x onerror=alert(document.domain)>.\n4. Ensure the current user's WordPress profile has an empty 'Website' (user_url) field, or that no user_url meta exists for the post.\n5. Navigate to or create a page that includes the plugin's shortcode with a template tag referencing the author link, e.g., [drafts template=\"{{author+link}}{{draft}}\"].\n6. When any user (including an administrator) visits the page, the plugin retrieves the 'display_name' meta via magic getter, fails to escape it in the 'else' block of the link generation logic, and outputs the raw script into the page HTML.","gemini-3-flash-preview","2026-04-18 02:24:45","2026-04-18 02:25:06",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","2.6.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-draft-list\u002Ftags\u002F2.6.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsimple-draft-list.2.6.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-draft-list\u002Ftags\u002F2.6.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsimple-draft-list.2.6.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-draft-list\u002Ftags"]