php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #68710 Use After Free Vulnerability in PHP's unserialize() (Still Exploitable)
Submitted: 2015-01-01 22:35 UTC Modified: 2015-02-10 15:27 UTC
From: stas@php.net Assigned: stas (profile)
Status: Closed Package: Reproducible crash
PHP Version: 5.4Git-2015-01-01 (Git) OS: *
Private report: No CVE-ID: 2015-0231
 [2015-01-01 22:35 UTC] stas@php.net
Description:
------------
Reported by stefan.esser@sektioneins.de:

Hi,

today I wanted to write a write up of the use after free vulnerability
in unserialize() that was fixed last month. Remember: I reported it on
2nd December, sent in a patch on 10th December, because Julien Pauli
asked for one. You later released updates. Unfortunately around that
time I was too busy to actually look into what you released and in a way
I thought it would be not necessary, because I sent in the correct patch.

However today I see that you changed my patch. Instead of using my
correct patch, you applied the following wrong patch.

        } else {
            /* object properties should include no integers */
            convert_to_string(key);
            if (zend_symtable_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key)
+ 1, (void **)&old_data)==SUCCESS) {
                var_push_dtor(var_hash, old_data);
            }
            zend_hash_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
&data,
                    sizeof data, NULL);
        }

There is a small but important difference to the patch I sent on 10th
December. You use zend_symtable_find instead of zend_hash_find from my
patch. Because of this change the fix is incomplete. It now detects
attacks that try to replace a key like "AAA", but it does not fix
attacks where the key is a numerical string like "123".

The reason for this is that we do not want integer keys in objects. That
is why the code was added in the first place. The object properties are
therefore inserted via zend_hash_update, instead of
zend_symtable_update. Therefore something like "123" will be inserted as
a string and not as a numerical 123. On the attempt to do the overwrite
attack you now check with zend_symtable_find(). This function will turn
the "123" into a numerical "123" and therefore not see that it is
already there. The protection will not be executed and therefore the
attack works in the same way as before.

Here is the updated diff that shows how the code is still vulnerable to
a slightly modified form of the attack:

--TEST--
Bug #XXXXX Use after free vulnerability in unserialize() (bypassing the
CVE-2014-8142 fix)
--FILE--
<?php
for ($i=4; $i<100; $i++) {
    $m = new StdClass();

    $u = array(1);

    $m->aaa = array(1,2,&$u,4,5);
    $m->bbb = 1;
    $m->ccc = &$u;
    $m->ddd = str_repeat("A", $i);

    $z = serialize($m);
    $z = str_replace("aaa", "123", $z);
    $z = str_replace("bbb", "123", $z);
    $y = unserialize($z);
    $z = serialize($y);
}
?>
===DONE===
--EXPECTF--
===DONE===

Please advice me of the new CVE for the previously incorrectly fixed
problem and a proposed timeframe for disclosure.

PS: I also advice educating the RedHat people, who get this information
forwarded so that they understand the seriousness of remote code
execution in unserialize(). Just because the manual says: bad idea does
not make it go away. Wordpress does still in 2015 unserialize() data
they pull over the network from their server. (Okay if possible they use
SSL for that, but we know for 1-2 years now that there are a bunch of
attackers capable of doing SSL MITM in the wild - nation state or just
another broken in-CA). And Wordpress is just one of so many examples.
Especially if you go away from OpenSource Code and look at all the
self-made websites/self-made CMSes that we see when auditing real world
code.

Kind regards and a productive 2015,
Stefan Esser




Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-01-03 00:52 UTC] stas@php.net
-Summary: se After Free Vulnerability in PHP's unserialize() (Still Exploitable) +Summary: Use After Free Vulnerability in PHP's unserialize() (Still Exploitable)
 [2015-01-19 05:09 UTC] stas@php.net
-CVE-ID: +CVE-ID: 2015-0231
 [2015-01-20 21:32 UTC] stas@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: stas
 [2015-01-20 21:32 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 [2015-02-10 15:24 UTC] derick@php.net
-Private report: No +Private report: Yes
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 11:01:30 2024 UTC