CVE-2026-25409

JAMstack Deployments <= 1.1.1 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The JAMstack Deployments plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.1.1. This makes it possible for authenticated attackers, with subscriber-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<=1.1.1
PublishedJanuary 28, 2026
Last updatedFebruary 26, 2026
Research Plan
Unverified

This research plan outlines the steps to investigate and exploit **CVE-2026-25409**, a missing authorization vulnerability in the **JAMstack Deployments** plugin (<= 1.1.1). --- ### 1. Vulnerability Summary The **JAMstack Deployments** plugin for WordPress allows users to trigger builds/deployment…

Show full research plan

This research plan outlines the steps to investigate and exploit CVE-2026-25409, a missing authorization vulnerability in the JAMstack Deployments plugin (<= 1.1.1).


1. Vulnerability Summary

The JAMstack Deployments plugin for WordPress allows users to trigger builds/deployments (e.g., to Netlify or Vercel) and manage deployment settings. The vulnerability exists because one or more AJAX or admin_post handlers fail to verify user capabilities (current_user_can). While these handlers may use nonces for CSRF protection, the nonces are often exposed to all authenticated users (including Subscribers) in the WordPress admin dashboard or admin bar. Because the plugin lacks a server-side capability check (e.g., manage_options), a Subscriber-level user can perform administrative actions, such as triggering a site deployment.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: Likely wp_ajax_jamstack_deploy or wp_ajax_jamstack_trigger_build (exact string to be confirmed).
  • Authentication: Authenticated (Subscriber level or higher).
  • Payload Parameters:
    • action: The vulnerable AJAX action.
    • _ajax_nonce or nonce: The CSRF token.
  • Preconditions: The plugin must be configured with a valid deployment webhook (e.g., a Netlify Build URL) for the effect to be observable.

3. Code Flow (Inferred)

  1. Registration: The plugin registers an AJAX action in its main class or an includes file:
    add_action( 'wp_ajax_jamstack_deploy', array( $this, 'trigger_deployment' ) );
    
  2. Handler Entry: When a Subscriber sends a request to admin-ajax.php?action=jamstack_deploy, the trigger_deployment function is executed.
  3. Vulnerable Logic:
    public function trigger_deployment() {
        // May check nonce:
        check_ajax_referer( 'jamstack_deploy_nonce', 'nonce' );
    
        // MISSING: if ( ! current_user_can( 'manage_options' ) ) wp_die();
    
        // Performs action:
        $webhook_url = get_option( 'jamstack_deploy_url' );
        wp_remote_post( $webhook_url );
        wp_send_json_success();
    }
    
  4. Sink: The wp_remote_post (or similar) executes regardless of the user's lack of administrative privileges.

4. Nonce Acquisition Strategy

The plugin likely enqueues scripts that include the nonce for the "Deploy Now" button found in the Admin Bar or Settings page.

  1. Identify Shortcode/UI: Search for wp_localize_script in the plugin code to find where the nonce is exposed.
  2. Create Trigger Page: If the nonce is only on specific pages, create a post:
    wp post create --post_type=page --post_status=publish --post_content='Testing Deployments'
  3. Browser Extraction:
    • Login as a Subscriber.
    • Navigate to the Dashboard or the newly created page.
    • Use browser_eval to find the nonce.
    • Inferred JS Variables: window.jamstack_vars?.nonce or window.wpJamstack?.deploy_nonce.
    • Verification: grep -r "wp_localize_script" . to find the exact object name.

5. Exploitation Strategy

  1. Discovery:
    • Run grep -rn "wp_ajax_" . to list all authenticated AJAX handlers.
    • Check each handler for the presence of current_user_can.
    • Identify the action responsible for triggering deployments (e.g., jamstack_deploy).
  2. Setup Mock Webhook:
    • To confirm the exploit, set the JAMstack deployment URL to a RequestBin-style URL or a local listener.
  3. Extract Nonce:
    • Access the site as a Subscriber and extract the nonce using browser_eval.
  4. Execute Unauthorized Action:
    • Send an authenticated POST request as the Subscriber to admin-ajax.php.
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    Cookie: [Subscriber Cookies]
    
    action=[IDENTIFIED_ACTION]&nonce=[EXTRACTED_NONCE]
    
  5. Verify Integrity Impact: Check if the remote webhook received a request or if the plugin's "Last Deployment" timestamp updated.

6. Test Data Setup

  1. Target User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  2. Plugin Configuration:
    wp option update jamstack_deploy_url "http://127.0.0.1:8080/mock-webhook" (Configure the plugin to point to a test endpoint).
  3. Nonce Visibility: Ensure the plugin settings allow the "Deploy" button to show in the admin bar (a common default).

7. Expected Results

  • The admin-ajax.php request returns a 200 OK or a JSON success message (e.g., {"success":true}).
  • The Subscriber, who should not have permission to trigger builds, successfully initiates a deployment.
  • The mock webhook receives a POST request originating from the WordPress server.

8. Verification Steps

  1. Webhook Logs: Confirm the external build trigger was hit.
  2. Database Check:
    wp option get jamstack_last_deploy_time (or equivalent meta key) to see if the timestamp updated.
  3. Access Control Confirmation:
    Attempt the same request without a cookie; it should fail (confirming it requires authentication). Attempt with Subscriber cookie; if it succeeds, the "Missing Authorization" (PR:L -> Admin Action) is confirmed.

9. Alternative Approaches

  • Settings Manipulation: If wp_ajax_jamstack_save_settings is also missing a capability check, an attacker could change the Deployment URL to an attacker-controlled server to intercept build tokens or perform SSRF.
  • REST API: Check register_rest_route. If the plugin uses the REST API, look for a permission_callback that returns true or checks for is_user_logged_in() instead of a specific capability.
    • grep -r "register_rest_route" .
    • Check if permission_callback is __return_true.
Research Findings
Static analysis — not yet PoC-verified

Summary

The JAMstack Deployments plugin for WordPress is vulnerable to unauthorized access because it fails to perform a server-side capability check in its AJAX handlers. This allow authenticated users with Subscriber-level permissions or higher to trigger unauthorized site deployments to external services like Netlify or Vercel.

Vulnerable Code

/* Path: wp-content/plugins/wp-jamstack-deployments/includes/class-wp-jamstack-deployments.php (Inferred) */

public function register_ajax_handlers() {
    add_action( 'wp_ajax_jamstack_deploy', array( $this, 'trigger_deployment' ) );
}

public function trigger_deployment() {
    // Nonce check is often present for CSRF, but does not substitute for Authorization
    check_ajax_referer( 'jamstack_deploy_nonce', 'nonce' );

    // VULNERABILITY: Missing check for current_user_can( 'manage_options' )

    $webhook_url = get_option( 'jamstack_deploy_url' );
    if ( $webhook_url ) {
        wp_remote_post( $webhook_url );
        wp_send_json_success( 'Deployment started.' );
    }
    wp_send_json_error( 'No URL configured.' );
}

Security Fix

--- wp-content/plugins/wp-jamstack-deployments/includes/class-wp-jamstack-deployments.php
+++ wp-content/plugins/wp-jamstack-deployments/includes/class-wp-jamstack-deployments.php
@@ -12,6 +12,10 @@
 	public function trigger_deployment() {
 		check_ajax_referer( 'jamstack_deploy_nonce', 'nonce' );
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( 'Unauthorized', 403 );
+		}
+
 		$webhook_url = get_option( 'jamstack_deploy_url' );
 		if ( $webhook_url ) {
 			wp_remote_post( $webhook_url );

Exploit Outline

1. Login to the WordPress site as a user with Subscriber-level privileges. 2. Locate the AJAX nonce required for the deployment action. This is typically exposed via `wp_localize_script` in the WordPress admin bar or the dashboard script variables (e.g., searching for 'jamstack' in the browser console). 3. Use a tool like `curl` or a browser developer console to send a POST request to `/wp-admin/admin-ajax.php`. 4. Include the parameter `action=jamstack_deploy` and the captured `nonce` in the POST body. 5. If successful, the server will respond with a JSON success message, and the plugin will initiate a POST request to the configured deployment webhook (e.g., Netlify/Vercel build URL).

Check if your site is affected.

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