CVE-2025-2104
Executive Summary
CVE-2025-2104 is a medium-severity vulnerability in the Page Builder: Pagelayer – Drag and Drop website builder plugin for WordPress. The flaw resides in the pagelayer_save_content() function, which fails to properly validate user capabilities before allowing post publication. This enables authenticated attackers with Contributor-level access or higher to bypass the WordPress post moderation workflow and publish posts directly, bypassing editorial review.
This vulnerability is specific to the WordPress ecosystem and affects the Pagelayer plugin. It does not involve Apache or Nginx server software. The attack vector is network-based, requires low complexity, and only requires low-level authenticated access (Contributor). The impact is limited to integrity (unauthorized post publication) with no confidentiality or availability impact.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is in a WordPress plugin distributed via the official WordPress plugin repository. Users should verify the integrity of their plugin installation by checking the plugin version and comparing checksums if available. The plugin is not distributed via NPM, PyPI, Go, or Ruby registries.
CVSS Breakdown & Threat Level
CVSS Score: 4.3 (Medium)
Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
- Attack Vector (AV): Network – The vulnerability can be exploited remotely over the network.
- Attack Complexity (AC): Low – No special conditions or complex attack sequences are required.
- Privileges Required (PR): Low – The attacker must have Contributor-level access or higher.
- User Interaction (UI): None – No user interaction is required for exploitation.
- Scope (S): Unchanged – The vulnerability does not affect resources beyond the vulnerable component.
- Confidentiality (C): None – No data is exposed.
- Integrity (I): Low – Unauthorized modification of post content is possible.
- Availability (A): None – No denial of service impact.
Threat Level: Medium. While the CVSS score is moderate, the ability to bypass moderation controls can lead to reputational damage, SEO poisoning, or the publication of malicious content.
Deep Dive & Mechanics
The vulnerability exists in the pagelayer_save_content() function within the Pagelayer plugin. This function is responsible for saving page/post content when using the drag-and-drop builder. The flaw is an Incorrect Authorization (CWE-862) issue.
Root Cause: The function does not properly check the edit_posts capability or the post status before saving. WordPress's wp_insert_post() function is called with insufficient validation of the user's role and the intended post status. Specifically, the function does not enforce the publish_posts capability check when the post status is set to 'publish'. This allows a Contributor (who normally can only submit posts for review) to publish directly.
Attack Flow:
- An authenticated user with Contributor-level access logs into WordPress.
- The attacker crafts a POST request to the Pagelayer AJAX endpoint (typically
admin-ajax.phpwith actionpagelayer_save_content). - The request includes the post ID and a
statusparameter set topublish. - The
pagelayer_save_content()function callswp_insert_post()with the provided status without verifying the user'spublish_postscapability. - The post is published immediately, bypassing the moderation queue.
Affected Versions: All versions of Pagelayer up to and including 1.9.8 are vulnerable.
Vulnerable vs. Patched Code
The vulnerable code likely resembles the following (simplified):
// Vulnerable code (pagelayer-save-content.php)
function pagelayer_save_content() {
// ... authentication check ...
$post_id = intval($_POST['post_id']);
$post_data = array(
'ID' => $post_id,
'post_content' => wp_kses_post($_POST['content']),
'post_status' => sanitize_text_field($_POST['status']), // No capability check
);
wp_insert_post($post_data);
// ...
}
A patched version would include a capability check:
// Patched code
function pagelayer_save_content() {
// ... authentication check ...
$post_id = intval($_POST['post_id']);
$post_status = sanitize_text_field($_POST['status']);
// Check if user can publish posts
if ($post_status === 'publish' && !current_user_can('publish_posts')) {
wp_die('Unauthorized', 403);
}
$post_data = array(
'ID' => $post_id,
'post_content' => wp_kses_post($_POST['content']),
'post_status' => $post_status,
);
wp_insert_post($post_data);
// ...
}
Exploitation & Lab Setup
Red Team
Prerequisites:
- A WordPress installation with Pagelayer plugin version 1.9.8 or earlier.
- A user account with Contributor-level privileges.
Exploitation Steps:
Obtain a valid nonce and cookie: Log in as a Contributor and obtain the WordPress nonce and authentication cookie.
Craft the exploit request: Send a POST request to
wp-admin/admin-ajax.phpwith the following parameters:action:pagelayer_save_contentpost_id: ID of an existing draft post (or create one)content: HTML content for the poststatus:publish_wpnonce: Valid nonce
Example using cURL:
curl -X POST https://target.com/wp-admin/admin-ajax.php \
-H "Cookie: wordpress_logged_in_<hash>=<cookie>" \
-d "action=pagelayer_save_content&post_id=123&content=<p>Exploited</p>&status=publish&_wpnonce=<nonce>"
Expected Result: The post with ID 123 will be published immediately, bypassing moderation.
Blue Team
Detection and Prevention:
Monitor AJAX requests: Look for
admin-ajax.phprequests withaction=pagelayer_save_contentandstatus=publishfrom users with Contributor role.Audit user capabilities: Regularly review user roles and ensure Contributors do not have unexpected capabilities.
Implement Web Application Firewall (WAF) rules: Block requests to
pagelayer_save_contentwherestatusispublishand the user role is Contributor.Log analysis: Search for successful post status changes from 'draft' to 'publish' by users who are not Editors or Administrators.
Indicators of Compromise (IOCs)
- File:
/wp-content/plugins/pagelayer/pagelayer-save-content.php(version 1.9.8 or earlier) - Log Entry:
admin-ajax.phpPOST request withaction=pagelayer_save_contentandstatus=publishfrom a Contributor user. - Database: Posts in the
wp_poststable withpost_status = 'publish'that were created by a user withwp_capabilitiescontainingcontributor. - Network: POST requests to
/wp-admin/admin-ajax.phpwith body containingpagelayer_save_content.
Detection Engineering
YARA Rule
rule CVE_2025_2104_Pagelayer_Plugin {
meta:
description = "Detects Pagelayer plugin version 1.9.8 or earlier vulnerable to CVE-2025-2104"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-2104"
hash = "unknown"
strings:
$vuln_func = "pagelayer_save_content"
$version_1 = "Version: 1.9.8"
$version_2 = "Version: 1.9."
$version_3 = "Version: 1.8."
$version_4 = "Version: 1.7."
$version_5 = "Version: 1.6."
$version_6 = "Version: 1.5."
$version_7 = "Version: 1.4."
$version_8 = "Version: 1.3."
$version_9 = "Version: 1.2."
$version_10 = "Version: 1.1."
$version_11 = "Version: 1.0."
condition:
$vuln_func and any of ($version_*)
}
Sigma Rule
title: CVE-2025-2104 Pagelayer Unauthorized Post Publication
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects exploitation attempts of CVE-2025-2104 in Pagelayer WordPress plugin
author: Valters IT
date: 2025-04-10
references:
- https://nvd.nist.gov/vuln/detail/CVE-2025-2104
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/wp-admin/admin-ajax.php'
cs-uri-query|contains: 'action=pagelayer_save_content'
cs-uri-query|contains: 'status=publish'
condition: selection
falsepositives:
- Legitimate use by Editors/Administrators
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2025-2104 Pagelayer Unauthorized Post Publication Attempt";
flow:to_server,established;
content:"POST"; http_method;
content:"/wp-admin/admin-ajax.php"; http_uri;
content:"action=pagelayer_save_content"; http_client_body;
content:"status=publish"; http_client_body;
classtype:attempted-admin;
sid:1000001;
rev:1;
reference:cve,2025-2104;
)
Remediation & Mitigation
Immediate Actions:
- Update the plugin: Upgrade Pagelayer to version 1.9.9 or later as soon as a patch is released. Check the WordPress plugin repository for updates.
- Disable the plugin: If an update is not available, temporarily deactivate Pagelayer until a patch is applied.
- Restrict user roles: Remove Contributor-level access from users who do not require it. Consider using a role editor plugin to further restrict capabilities.
Long-term Mitigations:
- Implement a Web Application Firewall (WAF): Use a WAF to block malicious requests targeting
pagelayer_save_content. - Enable logging: Ensure WordPress logging is enabled to capture AJAX requests and post status changes.
- Regular security audits: Periodically review user roles and capabilities.
- Monitor for plugin updates: Subscribe to security advisories for WordPress plugins.
Workaround: If patching is not immediately possible, add the following code to the theme's functions.php file to block the vulnerable function:
add_action('init', function() {
if (current_user_can('contributor') && isset($_POST['action']) && $_POST['action'] === 'pagelayer_save_content') {
wp_die('Access denied', 403);
}
});
Valters IT
Securing Digital Frontiers
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.