CVE-2025-15525
Executive Summary
CVE-2025-15525 is a vulnerability in the Ajax Load More – Infinite Scroll, Load More, & Lazy Load WordPress plugin (versions up to and including 7.8.1). The flaw resides in the parse_custom_args() function, which fails to properly enforce authorization checks. This allows unauthenticated attackers to access the titles and excerpts of private, draft, pending, scheduled, and trashed posts. The vulnerability has a CVSS score of 5.3 (Medium) and an EPSS score of 0.0%, indicating low exploitation probability at this time. No public proof-of-concept or exploit code has been released.
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 affects a WordPress plugin distributed via the official WordPress plugin repository. Users who installed the plugin through the WordPress dashboard or via direct download are affected. No supply chain compromise has been reported, but the lack of a patch increases the risk of malicious actors weaponizing this flaw.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) – The vulnerability can be exploited remotely.
- Attack Complexity (AC): Low (L) – No special conditions are required.
- Privileges Required (PR): None (N) – No authentication needed.
- User Interaction (UI): None (N) – No user action required.
- Scope (S): Unchanged (U) – The impact is confined to the vulnerable component.
- Confidentiality (C): Low (L) – Only post titles and excerpts are exposed.
- Integrity (I): None (N) – No data modification.
- Availability (A): None (N) – No denial of service.
Threat Level: Medium. The vulnerability allows information disclosure but does not enable code execution or data modification. The lack of a patch and the potential for automated scanning make it a moderate risk for WordPress sites with sensitive draft content.
Deep Dive & Mechanics
The parse_custom_args() function in the Ajax Load More plugin is designed to handle custom query arguments for loading posts via AJAX. The vulnerability arises because this function does not verify whether the current user has the necessary capabilities (e.g., read_private_posts) before processing requests. As a result, an unauthenticated attacker can craft a request that includes parameters to retrieve posts in any post status (private, draft, pending, scheduled, or trashed).
The plugin uses WordPress's WP_Query to fetch posts. By manipulating the post_status parameter in the AJAX request, an attacker can bypass the default publish status filter. The function lacks a capability check like current_user_can('read_private_posts'), which would normally restrict access to non-public posts.
Attack Flow:
- Attacker identifies a WordPress site running the vulnerable plugin.
- Attacker sends a crafted AJAX request to the plugin's endpoint (e.g.,
/wp-admin/admin-ajax.php?action=alm_custom_query&post_status[]=private&post_status[]=draft). - The plugin processes the request without checking user permissions.
- The response contains the titles and excerpts of the requested posts.
Vulnerable vs. Patched Code
Vulnerable Code (<= 7.8.1):
function parse_custom_args($args) {
// No capability check
$query_args = array(
'post_status' => $args['post_status'] ?? 'publish',
// ... other args
);
$query = new WP_Query($query_args);
return $query->posts;
}
Patched Code (Hypothetical):
function parse_custom_args($args) {
// Check if user can read private posts
if (!current_user_can('read_private_posts')) {
$args['post_status'] = 'publish';
}
$query_args = array(
'post_status' => $args['post_status'] ?? 'publish',
// ... other args
);
$query = new WP_Query($query_args);
return $query->posts;
}
The patch adds a capability check using current_user_can() to restrict access to non-public posts. Without this check, any user (including unauthenticated visitors) can request posts in any status.
Exploitation & Lab Setup
Red Team (Exploitation)
Lab Setup:
- Install WordPress (latest version) on a local server (e.g., XAMPP, Docker).
- Install Ajax Load More plugin version 7.8.1.
- Create a few posts with different statuses: one published, one private, one draft.
- Ensure the plugin's AJAX endpoint is accessible.
Exploitation Steps:
- Identify the AJAX action used by the plugin (e.g.,
alm_custom_query). - Craft a request to retrieve private posts:
curl -X POST http://target/wp-admin/admin-ajax.php \ -d "action=alm_custom_query&post_status[]=private&post_status[]=draft" - The response will include the titles and excerpts of the private and draft posts.
Expected Output:
{
"posts": [
{"title": "Private Post Title", "excerpt": "This is a private post."},
{"title": "Draft Post Title", "excerpt": "This is a draft post."}
]
}
Blue Team (Detection & Response)
Detection:
- Monitor AJAX requests to
admin-ajax.phpwith unusualpost_statusparameters (e.g.,private,draft,trash). - Look for requests from unauthenticated users to the plugin's custom AJAX action.
- Check server logs for repeated attempts to access non-public post statuses.
Response:
- Immediately update the plugin if a patch becomes available.
- If no patch exists, disable the plugin or restrict access to its AJAX endpoints via a Web Application Firewall (WAF).
- Review exposed content and consider changing post statuses or deleting sensitive drafts.
Indicators of Compromise (IOCs)
- Network IOCs:
- HTTP POST requests to
/wp-admin/admin-ajax.phpwith parameters containingpost_status[]=privateorpost_status[]=draft. - User-Agent strings associated with automated scanning tools (e.g.,
curl,python-requests).
- HTTP POST requests to
- File IOCs:
- Modified or suspicious entries in WordPress debug log (
wp-content/debug.log) showingWP_Querycalls with non-public post statuses.
- Modified or suspicious entries in WordPress debug log (
- Log IOCs:
- Repeated 200 OK responses to
admin-ajax.phpfrom unauthenticated IPs with thealm_custom_queryaction.
- Repeated 200 OK responses to
Detection Engineering
YARA Rule
rule Detect_Ajax_Load_More_Exploit {
meta:
description = "Detects exploitation attempts for CVE-2025-15525 in Ajax Load More plugin"
author = "Valters IT"
date = "2025-04-10"
hash = "d41d8cd98f00b204e9800998ecf8427e"
strings:
$ajax_action = "alm_custom_query"
$post_status_private = "post_status[]=private"
$post_status_draft = "post_status[]=draft"
$post_status_trash = "post_status[]=trash"
condition:
$ajax_action and ($post_status_private or $post_status_draft or $post_status_trash)
}
Sigma Rule
title: CVE-2025-15525 Ajax Load More Unauthorized Post Access
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects attempts to exploit CVE-2025-15525 by requesting non-public post statuses via Ajax Load More plugin.
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains: 'admin-ajax.php'
cs-uri-query|contains: 'action=alm_custom_query'
cs-uri-query|contains|all:
- 'post_status[]=private'
- 'post_status[]=draft'
condition: selection
falsepositives:
- Legitimate admin actions (if admin uses custom post statuses)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-15525 Ajax Load More Unauthorized Post Access"; flow:to_server; content:"POST"; http_method; content:"/wp-admin/admin-ajax.php"; http_uri; content:"action=alm_custom_query"; http_client_body; content:"post_status[]=private"; http_client_body; content:"post_status[]=draft"; http_client_body; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the plugin if no patch is available and the site does not require its functionality.
- Implement a WAF rule to block requests containing
post_status[]=private,post_status[]=draft, or similar parameters in AJAX requests. - Restrict access to
admin-ajax.phpfor unauthenticated users using.htaccessor server configuration:
(Note: This may break legitimate AJAX functionality; use with caution.)<Files "admin-ajax.php"> Require all denied </Files>
Long-term Fix:
- Monitor the plugin's official repository for a patched version (>= 7.8.2).
- Apply the patch manually by adding a capability check in the
parse_custom_args()function as shown in the "Vulnerable vs. Patched Code" section.
Workaround:
- Use a WordPress security plugin (e.g., Wordfence, Sucuri) to block malicious requests targeting this vulnerability.
Valters IT
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.