Fri, 30 Mar 2007


A helper function used by the printf() PHP function family returns a unsigned 63 bit long, but the result is internally stored in 32 bit ints. Because of the 32 bit truncation the resulting ints can be negative which is not catched by the calling code in differen code paths. This can result in two different exploitable memory corruptions.

Affected versions

Affected are PHP 4 < 4.4.5 and PHP 5 < 5.2.1

Detailed information

The printf() PHP function family internally uses the php_formatted_print() function that parses the format string and handles the embedded format specifiers. The function supports the usual format string syntax with selectable argument numbers via the $ modifier and widths and precisions. It will only accept positive a argument number, width or precision. All three are extracted by the use of the php_sprintf_getnumber() function.

inline static long
php_sprintf_getnumber(char *buffer, int *pos)
    char *endptr;
    register long num = strtol(&buffer[*pos], &endptr, 10);
    register int i = 0;

    if (endptr != NULL) {
        i = (endptr - &buffer[*pos]);
    PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i));
    *pos += i;
    return num;

Unfortunately the function works internally with the long variable type that is 64 bit wide on 64 bit systems. Because the calling code ensures that the number starts with a digit and not with a minus sign the result will always be a positive long number. However argument number, width and precision are stored as ints and therefore a 64 to 32 bit truncation occurs that can result in negative numbers.

This leads to two problems. First the argument number is only checked against an upper limit and because the code assumes that negative numbers are not possible it will not handle them correctly, which results in arbitrary memory addresses being referencable on 64 bit systems. This can result in the disclosure of arbitrary memory addresses or in exploitable memory corruptions.

The second problem introduced by this casting vulnerability is within the php_sprintf_appendstring() subfunction that handles the s format specifier. When a width and a precision of -1 are specified this will result in the buffer postition being decreased by one. By repeating the pattern multiple times it is possible to move the buffer posititon into to heap area infront of the actual buffer. Once the internal buffer pointer if over something like a heap control structure they can be overwritten with normal characters inside the format string.

Both exploit paths allow for code execution, however the second path is much easier and stable to exploit. Especially from remote.

Unlike C Format String Vulnerabilities PHP Format String Vulnerabilities only work on 64 bit systems and only when there is atleast one parameter after the format string in the printf() alike function.

Proof of concept, exploit or instructions to reproduce

POC will be released during the next week


Because the described problem was fixed in PHP 4.4.5 and PHP 5.2.1 after we reported it to the PHP developers, the newborn class of PHP Application Format String Vulnerabilities should be already dead again.