CVE-2026-57418

Sprout Invoices – Client Invoicing & Estimates <= 20.8.13 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
20.8.14
Patched in
7d
Time to patch

Description

The Sprout Invoices – Client Invoicing & Estimates plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 20.8.13. 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<=20.8.13
PublishedJuly 8, 2026
Last updatedJuly 14, 2026
Affected pluginsprout-invoices

What Changed in the Fix

Changes introduced in v20.8.14

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-57418 (Missing Authorization) ## 1. Vulnerability Summary The **Sprout Invoices – Client Invoicing & Estimates** plugin (version <= 20.8.13) is vulnerable to missing authorization in several AJAX handlers. Specifically, functions registered under the `wp_ajax_…

Show full research plan

Exploitation Research Plan: CVE-2026-57418 (Missing Authorization)

1. Vulnerability Summary

The Sprout Invoices – Client Invoicing & Estimates plugin (version <= 20.8.13) is vulnerable to missing authorization in several AJAX handlers. Specifically, functions registered under the wp_ajax_ prefix in SI_Settings_API and SI_Estimates/SI_Invoices fail to verify the user's capability (authorization) before performing actions. This allows an authenticated user with Subscriber-level access to perform unauthorized actions, such as modifying plugin settings or sending arbitrary notifications.

2. Attack Vector Analysis

  • Target Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Action: si_gtag_option_action (and potentially si_stripe_option_action or sa_send_est_notification).
  • Required Authentication: Subscriber or higher.
  • Payload Parameter: option (for si_gtag_option_action) or sa_metabox_recipients (for notification spam).
  • Security Check: The functions verify a nonce (usually passed as security or nonce) but do not check for the manage_sprout_invoices_options or edit_sprout_invoices capabilities.

3. Code Flow

  1. Registration: In controllers/admin/Settings_API.php,
Research Findings
Static analysis — not yet PoC-verified

Summary

The Sprout Invoices plugin for WordPress is vulnerable to unauthorized access because multiple AJAX and REST API handlers fail to perform capability checks. Authenticated users, including those with subscriber-level permissions, can perform unauthorized actions such as sending arbitrary notifications for invoices and estimates or managing plugin add-ons.

Vulnerable Code

// controllers/estimates/Estimates.php:127
	public static function maybe_send_notification() {
		// ...
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_notification_nonce'] ) ), SI_Controller::NONCE ) ) {
			self::ajax_fail( 'Not going to fall for it!' ); }

		if ( ! isset( $_REQUEST['sa_send_metabox_doc_id'] ) ) {
			self::ajax_fail( 'Forget something (id)?' ); }

		if ( get_post_type( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) ) ) !== SI_Estimate::POST_TYPE ) {
			return;
		}

		$recipients = ( isset( $_REQUEST['sa_metabox_recipients'] ) )
			? array_map( 'sanitize_text_field', ( wp_unslash( $_REQUEST['sa_metabox_recipients'] ) ) )
			: array();

---

// controllers/invoices/Invoices.php:115
	public static function maybe_send_notification() {
		// ...
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_notification_nonce'] ) ), SI_Controller::NONCE ) ) {
			self::ajax_fail( 'Not going to fall for it!' );
		}

		if ( ! isset( $_REQUEST['sa_send_metabox_doc_id'] ) ) {
			self::ajax_fail( 'Forget something (id)?' );
		}

		if ( get_post_type( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) ) ) !== SI_Invoice::POST_TYPE ) {
			return;
		}

		$recipients = ( isset( $_REQUEST['sa_metabox_recipients'] ) ) ? array_map( 'sanitize_text_field', ( wp_unslash( $_REQUEST['sa_metabox_recipients'] ) ) ) : array();

---

// controllers/admin/Settings_API.php:673
				'callback' => function () {
					// phpcs:disable WordPress.Security.NonceVerification.Missing -- WordPress REST API validates authentication via X-WP-Nonce header; permission_callback enforces capability
					$_POST = stripslashes_deep( $_POST );

					if ( isset( $_POST['activate'] ) ) {
						SA_Addons::activate_addon( sanitize_text_field( wp_unslash( $_POST['activate'] ) ) );
					}

					if ( isset( $_POST['deactivate'] ) ) {
						SA_Addons::deactivate_addon( sanitize_text_field( wp_unslash( $_POST['deactivate'] ) ) );

					}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.13/controllers/estimates/Estimates.php /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.14/controllers/estimates/Estimates.php
--- /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.13/controllers/estimates/Estimates.php	2026-04-30 17:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.14/controllers/estimates/Estimates.php	2026-06-04 15:16:58.000000000 +0000
@@ -141,10 +141,33 @@
 		if ( ! isset( $_REQUEST['sa_send_metabox_doc_id'] ) ) {
 			self::ajax_fail( 'Forget something (id)?' ); }
 
-		if ( get_post_type( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) ) ) !== SI_Estimate::POST_TYPE ) {
+		$doc_id    = absint( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) );
+		$post_type = get_post_type( $doc_id );
+
+		if ( SI_Invoice::POST_TYPE === $post_type ) {
 			return;
 		}
 
+		if ( SI_Estimate::POST_TYPE !== $post_type ) {
+			self::ajax_fail( 'Not an invoice or estimate.' );
+		}
+
+		if ( ! current_user_can( 'edit_post', $doc_id ) ) {
+			self::ajax_fail( 'You do not have permission to send this notification.' );
+		}
+
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.13/controllers/invoices/Invoices.php /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.14/controllers/invoices/Invoices.php
--- /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.13/controllers/invoices/Invoices.php	2026-04-30 17:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/sprout-invoices/20.8.14/controllers/invoices/Invoices.php	2026-06-04 15:16:58.000000000 +0000
@@ -125,10 +125,33 @@
 			self::ajax_fail( 'Forget something (id)?' );
 		}
 
-		if ( get_post_type( sanitize_text_field( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) ) ) !== SI_Invoice::POST_TYPE ) {
+		$doc_id    = absint( wp_unslash( $_REQUEST['sa_send_metabox_doc_id'] ) );
+		$post_type = get_post_type( $doc_id );
+
+		if ( SI_Estimate::POST_TYPE === $post_type ) {
 			return;
 		}
 
+		if ( SI_Invoice::POST_TYPE !== $post_type ) {
+			self::ajax_fail( 'Not an invoice or estimate.' );
+		}
+
+		if ( ! current_user_can( 'edit_post', $doc_id ) ) {
+			self::ajax_fail( 'You do not have permission to send this notification.' );
+		}

Exploit Outline

To exploit this vulnerability, an attacker first authenticates with a low-privileged account (such as a Subscriber). They then extract the global nonce from the frontend via the `si_js_object.security` variable, which is exposed to all logged-in users. Using this nonce, the attacker can make POST requests to `/wp-admin/admin-ajax.php` with the action `sa_send_est_notification`. By providing an arbitrary invoice or estimate ID in the `sa_send_metabox_doc_id` parameter and a list of target emails in `sa_metabox_recipients[]`, the attacker can trigger the plugin to send official invoice/estimate notifications to any address, potentially leading to information disclosure or spam.

Check if your site is affected.

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