diff options
author | Anatol Belski <ab@php.net> | 2014-04-17 10:48:14 +0200 |
---|---|---|
committer | Anatol Belski <ab@php.net> | 2014-04-17 10:48:14 +0200 |
commit | 5328d4289946e260232f3195ba2e0f0eb173d5ef (patch) | |
tree | 1b45041613881715d91bde6b9cab41b1f0e9bebc | |
parent | 7a5f1663c6775bbdaf870e4c71ef8813d5d13179 (diff) | |
download | php-git-5328d4289946e260232f3195ba2e0f0eb173d5ef.tar.gz |
Fixed bug #67072 Echoing unserialized "SplFileObject" crash
The actual issue lays in the unserializer code which doesn't honor
the unserialize callback. By contrast, the serialize callback is
respected. This leads to the situation that even if a class has
disabled the serialization explicitly, user could still construct
a vulnerable string which would result bad things when trying
to unserialize.
This conserns also the classes implementing Serializable as well
as some core classes disabling serialize/unserialize callbacks
explicitly (PDO, SimpleXML, SplFileInfo and co). As of now, the
flow is first to call the unserialize callback (if available),
then call __wakeup. If the unserialize callback returns with no
success, no object is instantiated. This makes the scheme used
by internal classes effective, to disable unserialize just assign
zend_class_unserialize_deny as callback.
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ext/standard/tests/serialize/005.phpt | 1 | ||||
-rw-r--r-- | ext/standard/tests/serialize/bug67072.phpt | 13 | ||||
-rw-r--r-- | ext/standard/var_unserializer.c | 75 | ||||
-rw-r--r-- | ext/standard/var_unserializer.re | 11 |
5 files changed, 70 insertions, 34 deletions
@@ -6,6 +6,10 @@ PHP NEWS . Fixed bug #67081 (DOMDocumentType->internalSubset returns entire DOCTYPE tag, not only the subset). (Anatol) +- Standard: + . Fixed bug #67072 (Echoing unserialized "SplFileObject" crash). (Anatol) + + ?? ??? 2014, PHP 5.4.28 - Core: diff --git a/ext/standard/tests/serialize/005.phpt b/ext/standard/tests/serialize/005.phpt index e7b23db701..87a3fb3ab7 100644 --- a/ext/standard/tests/serialize/005.phpt +++ b/ext/standard/tests/serialize/005.phpt @@ -156,6 +156,7 @@ object(TestNAOld)#%d (0) { } ===NANew=== unserializer(TestNANew) +TestNew::unserialize() TestNew::__wakeup() object(TestNANew)#%d (0) { } diff --git a/ext/standard/tests/serialize/bug67072.phpt b/ext/standard/tests/serialize/bug67072.phpt new file mode 100644 index 0000000000..3e7d33ba8a --- /dev/null +++ b/ext/standard/tests/serialize/bug67072.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #67072 Echoing unserialized "SplFileObject" crash +--FILE-- +<?php + echo unserialize('O:13:"SplFileObject":1:{s:9:"*filename";s:15:"/home/flag/flag";}'); +?> +===DONE== +--EXPECTF-- +Fatal error: Uncaught exception 'Exception' with message 'Unserialization of 'SplFileObject' is not allowed' in %sbug67072.php:2 +Stack trace: +#0 %sbug67072.php(2): unserialize('O:13:"SplFileOb...') +#1 {main} + thrown in %sbug67072.php on line 2 diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index e39a3505cd..8db00c239f 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,10 +1,10 @@ -/* Generated by re2c 0.13.5 on Mon Jul 29 17:57:26 2013 */ +/* Generated by re2c 0.13.5 on Thu Apr 17 10:03:26 2014 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2014 The PHP Group | + | Copyright (c) 1997-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -396,7 +396,12 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) (*p) += 2; - object_init_ex(*rval, ce); + if (ce->unserialize == NULL) { + object_init_ex(*rval, ce); + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) { + return 0; + } + return elements; } @@ -408,6 +413,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) zval *retval_ptr = NULL; zval fname; + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { return 0; } @@ -457,7 +466,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 461 "ext/standard/var_unserializer.c" +#line 470 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -517,9 +526,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 812 "ext/standard/var_unserializer.re" +#line 821 "ext/standard/var_unserializer.re" { return 0; } -#line 523 "ext/standard/var_unserializer.c" +#line 532 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -562,13 +571,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 806 "ext/standard/var_unserializer.re" +#line 815 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 572 "ext/standard/var_unserializer.c" +#line 581 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -598,7 +607,7 @@ yy20: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 660 "ext/standard/var_unserializer.re" +#line 669 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -744,7 +753,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 748 "ext/standard/var_unserializer.c" +#line 757 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -769,7 +778,7 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 652 "ext/standard/var_unserializer.re" +#line 661 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -777,7 +786,7 @@ yy27: return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 781 "ext/standard/var_unserializer.c" +#line 790 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -798,7 +807,7 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 632 "ext/standard/var_unserializer.re" +#line 641 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -818,7 +827,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 822 "ext/standard/var_unserializer.c" +#line 831 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -839,7 +848,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 603 "ext/standard/var_unserializer.re" +#line 612 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -868,7 +877,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 872 "ext/standard/var_unserializer.c" +#line 881 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -889,7 +898,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 575 "ext/standard/var_unserializer.re" +#line 584 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -917,7 +926,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 921 "ext/standard/var_unserializer.c" +#line 930 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1005,7 +1014,7 @@ yy61: } yy63: ++YYCURSOR; -#line 565 "ext/standard/var_unserializer.re" +#line 574 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1015,7 +1024,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1019 "ext/standard/var_unserializer.c" +#line 1028 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1074,7 +1083,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 550 "ext/standard/var_unserializer.re" +#line 559 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1089,7 +1098,7 @@ yy73: return 1; } -#line 1093 "ext/standard/var_unserializer.c" +#line 1102 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1116,7 +1125,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 523 "ext/standard/var_unserializer.re" +#line 532 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1143,7 +1152,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1147 "ext/standard/var_unserializer.c" +#line 1156 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1151,24 +1160,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 516 "ext/standard/var_unserializer.re" +#line 525 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1162 "ext/standard/var_unserializer.c" +#line 1171 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 509 "ext/standard/var_unserializer.re" +#line 518 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1172 "ext/standard/var_unserializer.c" +#line 1181 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1191,7 +1200,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 486 "ext/standard/var_unserializer.re" +#line 495 "ext/standard/var_unserializer.re" { long id; @@ -1214,7 +1223,7 @@ yy91: return 1; } -#line 1218 "ext/standard/var_unserializer.c" +#line 1227 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1237,7 +1246,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 465 "ext/standard/var_unserializer.re" +#line 474 "ext/standard/var_unserializer.re" { long id; @@ -1258,9 +1267,9 @@ yy97: return 1; } -#line 1262 "ext/standard/var_unserializer.c" +#line 1271 "ext/standard/var_unserializer.c" } -#line 814 "ext/standard/var_unserializer.re" +#line 823 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 76c501e1b5..1f71bb759d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -400,7 +400,12 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) (*p) += 2; - object_init_ex(*rval, ce); + if (ce->unserialize == NULL) { + object_init_ex(*rval, ce); + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) { + return 0; + } + return elements; } @@ -412,6 +417,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) zval *retval_ptr = NULL; zval fname; + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { return 0; } |