CVE-2026-22490

Bulk Landing Page Creator for WordPress LPagery <= 2.4.9 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.4.10
Patched in
8d
Time to patch

Description

The Bulk Page Generator – LPagery plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.4.9. This makes it possible for authenticated attackers, with Contributor-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.4.9
PublishedJanuary 7, 2026
Last updatedJanuary 14, 2026
Affected pluginlpagery

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-22490 ## 1. Vulnerability Summary **CVE-2026-22490** is a Missing Authorization vulnerability in the **Bulk Page Generator – LPagery** plugin for WordPress (versions <= 2.4.9). The vulnerability exists because the plugin registers several AJAX handlers using …

Show full research plan

Exploitation Research Plan - CVE-2026-22490

1. Vulnerability Summary

CVE-2026-22490 is a Missing Authorization vulnerability in the Bulk Page Generator – LPagery plugin for WordPress (versions <= 2.4.9). The vulnerability exists because the plugin registers several AJAX handlers using the wp_ajax_ hook but fails to perform capability checks (e.g., current_user_can('manage_options')) within the callback functions. This allows any authenticated user with at least Contributor level access to execute administrative actions, such as deleting all pages generated by the plugin or modifying certain plugin settings.

2. Attack Vector Analysis

  • Vulnerable Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: lpagery_delete_all_generated (inferred) or lpagery_save_settings (inferred). We will target the deletion of generated pages as the primary PoC.
  • Payload Parameters:
    • action: lpagery_delete_all_generated
    • _ajax_nonce: A valid nonce for the lpagery_nonce action.
  • Authentication: Authenticated user with Contributor role.
  • Preconditions: The plugin must have been used to generate at least one page, and the Contributor user must be able to obtain a valid nonce.

3. Code Flow

  1. Entry Point: The user sends a POST request to /wp-admin/admin-ajax.php.
  2. Hook Registration: The plugin registers the action in its initialization (likely in includes/class-lpagery.php or a dedicated AJAX handler file):
    add_action( 'wp_ajax_lpagery_delete_all_generated', array( $this, 'delete_all_generated_pages' ) );
    
  3. Vulnerable Function: The function delete_all_generated_pages (inferred) is called.
  4. Nonce Check: The function typically calls check_ajax_referer( 'lpagery_nonce', '_ajax_nonce' );.
  5. Missing Auth Check: The function proceeds to delete pages (e.g., querying for posts with the _lpagery_generated meta key) without checking if the user has the manage_options capability.
  6. Sink: Database deletion via $wpdb->query or wp_delete_post.

4. Nonce Acquisition Strategy

While LPagery is an admin-focused plugin, it often enqueues its scripts and localizes nonces on all admin pages, or at least on pages accessible to all logged-in users (like the Dashboard).

  1. Identify Script Localization: Look for wp_localize_script in the plugin source (often in admin/class-lpagery-admin.php).
  2. Target Variable: Based on common LPagery patterns, the JS object is likely lpagery_vars or lpagery_obj.
  3. Acquisition Method:
    • Log in as the Contributor.
    • Navigate to the main WordPress Dashboard: /wp-admin/index.php.
    • Use browser_eval to extract the nonce.
    • JS Path: window.lpagery_vars?.nonce or window.lpagery_obj?.nonce.

5. Exploitation Strategy

  1. Setup: Create a Contributor user and generate dummy pages using LPagery (or manually add the meta key to existing pages).
  2. Nonce Extraction:
    • Navigate the browser to /wp-admin/index.php.
    • Execute browser_eval("window.lpagery_vars.nonce") to retrieve the _ajax_nonce.
  3. Execution:
    • Use http_request to send a POST request to the AJAX endpoint.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=lpagery_delete_all_generated&_ajax_nonce=[NONCE]
  4. Alternative Action: If deletion is restricted, attempt action=lpagery_ignore_notice or action=lpagery_save_settings.

6. Test Data Setup

  1. Create Pages:
    • Generate 5 pages via the LPagery "Bulk Creator" (if possible via CLI).
    • Alternatively, use WP-CLI to create pages with the specific meta key LPagery uses to track its pages:
      wp post create --post_type=page --post_title="LPagery Test Page" --post_status=publish --meta_input='{"_lpagery_generated":"1"}'
      
  2. Create Attacker:
    • Create a contributor: wp user create attacker attacker@example.com --role=contributor --user_pass=password123.

7. Expected Results

  • Response: The admin-ajax.php endpoint should return a 200 OK response, often with a JSON body like {"success":true} or a simple string 1.
  • Impact: All pages created in the "Test Data Setup" phase that have the _lpagery_generated meta key should be moved to the trash or permanently deleted.

8. Verification Steps

  1. Check Page Existence: Run WP-CLI to count pages with the LPagery meta key:
    wp post list --post_type=page --meta_key=_lpagery_generated --format=count
    
    The count should be 0 after a successful exploit.
  2. Check Trash: If the plugin trashes instead of deletes:
    wp post list --post_type=page --post_status=trash --meta_key=_lpagery_generated --format=count
    

9. Alternative Approaches

If lpagery_delete_all_generated is not the vulnerable action:

  • Target lpagery_update_settings: Attempt to change a plugin setting (e.g., lpagery_capability) to subscriber or contributor to grant even more access.
    • Body: action=lpagery_update_settings&_ajax_nonce=[NONCE]&option_name=...&option_value=...
  • Target lpagery_import_csv: If the plugin allows imports via AJAX, a contributor could potentially create thousands of spam pages or perform a Denial of Service by triggering large imports.
Research Findings
Static analysis — not yet PoC-verified

Summary

The LPagery plugin for WordPress (versions up to 2.4.9) is vulnerable to unauthorized access because it fails to perform capability checks in its AJAX handlers. This allows authenticated users with Contributor-level access to trigger administrative actions, such as deleting all generated pages, by providing a valid nonce available in the admin dashboard. The vulnerability stems from relying solely on nonce verification without verifying if the user has appropriate permissions like 'manage_options'.

Vulnerable Code

// In includes/class-lpagery-admin.php (inferred)
add_action( 'wp_ajax_lpagery_delete_all_generated', array( $this, 'delete_all_generated_pages' ) );

---

// In includes/class-lpagery-admin.php (inferred)
public function delete_all_generated_pages() {
    // Nonce is checked, but user capability is NOT checked.
    check_ajax_referer( 'lpagery_nonce', '_ajax_nonce' );

    // The function continues to delete pages without checking current_user_can('manage_options')
    $posts = get_posts(array(
        'post_type' => 'any',
        'meta_key' => '_lpagery_generated',
        'fields' => 'ids',
        'posts_per_page' => -1
    ));

    foreach ($posts as $post_id) {
        wp_delete_post($post_id, true);
    }
    wp_send_json_success();
}

Security Fix

--- a/includes/class-lpagery-admin.php
+++ b/includes/class-lpagery-admin.php
@@ -... @@
 	public function delete_all_generated_pages() {
 		check_ajax_referer( 'lpagery_nonce', '_ajax_nonce' );
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
+		}
+
 		$posts = get_posts( array(

Exploit Outline

An attacker with Contributor-level access can perform administrative tasks by exploiting the 'lpagery_delete_all_generated' AJAX action. The attacker first logs into the WordPress admin panel and retrieves the required AJAX nonce from the localized 'lpagery_vars' or 'lpagery_obj' script object found on the Dashboard. They then submit a POST request to '/wp-admin/admin-ajax.php' containing the 'action=lpagery_delete_all_generated' and '_ajax_nonce=[NONCE]' parameters. Because the plugin only verifies the nonce and not user capabilities, the request succeeds and deletes all content marked with the plugin's internal meta keys.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.