Skip to content

Commit 5328d42

Browse files
committed
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.
1 parent 7a5f166 commit 5328d42

File tree

5 files changed

+70
-34
lines changed

5 files changed

+70
-34
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug #67081 (DOMDocumentType->internalSubset returns entire DOCTYPE tag,
77
not only the subset). (Anatol)
88

9+
- Standard:
10+
. Fixed bug #67072 (Echoing unserialized "SplFileObject" crash). (Anatol)
11+
12+
913
?? ??? 2014, PHP 5.4.28
1014

1115
- Core:

ext/standard/tests/serialize/005.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ object(TestNAOld)#%d (0) {
156156
}
157157
===NANew===
158158
unserializer(TestNANew)
159+
TestNew::unserialize()
159160
TestNew::__wakeup()
160161
object(TestNANew)#%d (0) {
161162
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Bug #67072 Echoing unserialized "SplFileObject" crash
3+
--FILE--
4+
<?php
5+
echo unserialize('O:13:"SplFileObject":1:{s:9:"*filename";s:15:"/home/flag/flag";}');
6+
?>
7+
===DONE==
8+
--EXPECTF--
9+
Fatal error: Uncaught exception 'Exception' with message 'Unserialization of 'SplFileObject' is not allowed' in %sbug67072.php:2
10+
Stack trace:
11+
#0 %sbug67072.php(2): unserialize('O:13:"SplFileOb...')
12+
#1 {main}
13+
thrown in %sbug67072.php on line 2

ext/standard/var_unserializer.c

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
/* Generated by re2c 0.13.5 on Mon Jul 29 17:57:26 2013 */
1+
/* Generated by re2c 0.13.5 on Thu Apr 17 10:03:26 2014 */
22
#line 1 "ext/standard/var_unserializer.re"
33
/*
44
+----------------------------------------------------------------------+
55
| PHP Version 5 |
66
+----------------------------------------------------------------------+
7-
| Copyright (c) 1997-2014 The PHP Group |
7+
| Copyright (c) 1997-2013 The PHP Group |
88
+----------------------------------------------------------------------+
99
| This source file is subject to version 3.01 of the PHP license, |
1010
| 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)
396396

397397
(*p) += 2;
398398

399-
object_init_ex(*rval, ce);
399+
if (ce->unserialize == NULL) {
400+
object_init_ex(*rval, ce);
401+
} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) {
402+
return 0;
403+
}
404+
400405
return elements;
401406
}
402407

@@ -408,6 +413,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
408413
zval *retval_ptr = NULL;
409414
zval fname;
410415

416+
if (Z_TYPE_PP(rval) != IS_OBJECT) {
417+
return 0;
418+
}
419+
411420
if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) {
412421
return 0;
413422
}
@@ -457,7 +466,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
457466

458467

459468

460-
#line 461 "ext/standard/var_unserializer.c"
469+
#line 470 "ext/standard/var_unserializer.c"
461470
{
462471
YYCTYPE yych;
463472
static const unsigned char yybm[] = {
@@ -517,9 +526,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
517526
yych = *(YYMARKER = ++YYCURSOR);
518527
if (yych == ':') goto yy95;
519528
yy3:
520-
#line 812 "ext/standard/var_unserializer.re"
529+
#line 821 "ext/standard/var_unserializer.re"
521530
{ return 0; }
522-
#line 523 "ext/standard/var_unserializer.c"
531+
#line 532 "ext/standard/var_unserializer.c"
523532
yy4:
524533
yych = *(YYMARKER = ++YYCURSOR);
525534
if (yych == ':') goto yy89;
@@ -562,13 +571,13 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
562571
goto yy3;
563572
yy14:
564573
++YYCURSOR;
565-
#line 806 "ext/standard/var_unserializer.re"
574+
#line 815 "ext/standard/var_unserializer.re"
566575
{
567576
/* this is the case where we have less data than planned */
568577
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
569578
return 0; /* not sure if it should be 0 or 1 here? */
570579
}
571-
#line 572 "ext/standard/var_unserializer.c"
580+
#line 581 "ext/standard/var_unserializer.c"
572581
yy16:
573582
yych = *++YYCURSOR;
574583
goto yy3;
@@ -598,7 +607,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
598607
yych = *++YYCURSOR;
599608
if (yych != '"') goto yy18;
600609
++YYCURSOR;
601-
#line 660 "ext/standard/var_unserializer.re"
610+
#line 669 "ext/standard/var_unserializer.re"
602611
{
603612
size_t len, len2, len3, maxlen;
604613
long elements;
@@ -744,7 +753,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
744753

745754
return object_common2(UNSERIALIZE_PASSTHRU, elements);
746755
}
747-
#line 748 "ext/standard/var_unserializer.c"
756+
#line 757 "ext/standard/var_unserializer.c"
748757
yy25:
749758
yych = *++YYCURSOR;
750759
if (yych <= ',') {
@@ -769,15 +778,15 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
769778
yych = *++YYCURSOR;
770779
if (yych != '"') goto yy18;
771780
++YYCURSOR;
772-
#line 652 "ext/standard/var_unserializer.re"
781+
#line 661 "ext/standard/var_unserializer.re"
773782
{
774783

775784
INIT_PZVAL(*rval);
776785

777786
return object_common2(UNSERIALIZE_PASSTHRU,
778787
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
779788
}
780-
#line 781 "ext/standard/var_unserializer.c"
789+
#line 790 "ext/standard/var_unserializer.c"
781790
yy32:
782791
yych = *++YYCURSOR;
783792
if (yych == '+') goto yy33;
@@ -798,7 +807,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
798807
yych = *++YYCURSOR;
799808
if (yych != '{') goto yy18;
800809
++YYCURSOR;
801-
#line 632 "ext/standard/var_unserializer.re"
810+
#line 641 "ext/standard/var_unserializer.re"
802811
{
803812
long elements = parse_iv(start + 2);
804813
/* use iv() not uiv() in order to check data range */
@@ -818,7 +827,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
818827

819828
return finish_nested_data(UNSERIALIZE_PASSTHRU);
820829
}
821-
#line 822 "ext/standard/var_unserializer.c"
830+
#line 831 "ext/standard/var_unserializer.c"
822831
yy39:
823832
yych = *++YYCURSOR;
824833
if (yych == '+') goto yy40;
@@ -839,7 +848,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
839848
yych = *++YYCURSOR;
840849
if (yych != '"') goto yy18;
841850
++YYCURSOR;
842-
#line 603 "ext/standard/var_unserializer.re"
851+
#line 612 "ext/standard/var_unserializer.re"
843852
{
844853
size_t len, maxlen;
845854
char *str;
@@ -868,7 +877,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
868877
ZVAL_STRINGL(*rval, str, len, 0);
869878
return 1;
870879
}
871-
#line 872 "ext/standard/var_unserializer.c"
880+
#line 881 "ext/standard/var_unserializer.c"
872881
yy46:
873882
yych = *++YYCURSOR;
874883
if (yych == '+') goto yy47;
@@ -889,7 +898,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
889898
yych = *++YYCURSOR;
890899
if (yych != '"') goto yy18;
891900
++YYCURSOR;
892-
#line 575 "ext/standard/var_unserializer.re"
901+
#line 584 "ext/standard/var_unserializer.re"
893902
{
894903
size_t len, maxlen;
895904
char *str;
@@ -917,7 +926,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
917926
ZVAL_STRINGL(*rval, str, len, 1);
918927
return 1;
919928
}
920-
#line 921 "ext/standard/var_unserializer.c"
929+
#line 930 "ext/standard/var_unserializer.c"
921930
yy53:
922931
yych = *++YYCURSOR;
923932
if (yych <= '/') {
@@ -1005,7 +1014,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
10051014
}
10061015
yy63:
10071016
++YYCURSOR;
1008-
#line 565 "ext/standard/var_unserializer.re"
1017+
#line 574 "ext/standard/var_unserializer.re"
10091018
{
10101019
#if SIZEOF_LONG == 4
10111020
use_double:
@@ -1015,7 +1024,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
10151024
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
10161025
return 1;
10171026
}
1018-
#line 1019 "ext/standard/var_unserializer.c"
1027+
#line 1028 "ext/standard/var_unserializer.c"
10191028
yy65:
10201029
yych = *++YYCURSOR;
10211030
if (yych <= ',') {
@@ -1074,7 +1083,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
10741083
yych = *++YYCURSOR;
10751084
if (yych != ';') goto yy18;
10761085
++YYCURSOR;
1077-
#line 550 "ext/standard/var_unserializer.re"
1086+
#line 559 "ext/standard/var_unserializer.re"
10781087
{
10791088
*p = YYCURSOR;
10801089
INIT_PZVAL(*rval);
@@ -1089,7 +1098,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
10891098

10901099
return 1;
10911100
}
1092-
#line 1093 "ext/standard/var_unserializer.c"
1101+
#line 1102 "ext/standard/var_unserializer.c"
10931102
yy76:
10941103
yych = *++YYCURSOR;
10951104
if (yych == 'N') goto yy73;
@@ -1116,7 +1125,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
11161125
if (yych <= '9') goto yy79;
11171126
if (yych != ';') goto yy18;
11181127
++YYCURSOR;
1119-
#line 523 "ext/standard/var_unserializer.re"
1128+
#line 532 "ext/standard/var_unserializer.re"
11201129
{
11211130
#if SIZEOF_LONG == 4
11221131
int digits = YYCURSOR - start - 3;
@@ -1143,32 +1152,32 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
11431152
ZVAL_LONG(*rval, parse_iv(start + 2));
11441153
return 1;
11451154
}
1146-
#line 1147 "ext/standard/var_unserializer.c"
1155+
#line 1156 "ext/standard/var_unserializer.c"
11471156
yy83:
11481157
yych = *++YYCURSOR;
11491158
if (yych <= '/') goto yy18;
11501159
if (yych >= '2') goto yy18;
11511160
yych = *++YYCURSOR;
11521161
if (yych != ';') goto yy18;
11531162
++YYCURSOR;
1154-
#line 516 "ext/standard/var_unserializer.re"
1163+
#line 525 "ext/standard/var_unserializer.re"
11551164
{
11561165
*p = YYCURSOR;
11571166
INIT_PZVAL(*rval);
11581167
ZVAL_BOOL(*rval, parse_iv(start + 2));
11591168
return 1;
11601169
}
1161-
#line 1162 "ext/standard/var_unserializer.c"
1170+
#line 1171 "ext/standard/var_unserializer.c"
11621171
yy87:
11631172
++YYCURSOR;
1164-
#line 509 "ext/standard/var_unserializer.re"
1173+
#line 518 "ext/standard/var_unserializer.re"
11651174
{
11661175
*p = YYCURSOR;
11671176
INIT_PZVAL(*rval);
11681177
ZVAL_NULL(*rval);
11691178
return 1;
11701179
}
1171-
#line 1172 "ext/standard/var_unserializer.c"
1180+
#line 1181 "ext/standard/var_unserializer.c"
11721181
yy89:
11731182
yych = *++YYCURSOR;
11741183
if (yych <= ',') {
@@ -1191,7 +1200,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
11911200
if (yych <= '9') goto yy91;
11921201
if (yych != ';') goto yy18;
11931202
++YYCURSOR;
1194-
#line 486 "ext/standard/var_unserializer.re"
1203+
#line 495 "ext/standard/var_unserializer.re"
11951204
{
11961205
long id;
11971206

@@ -1214,7 +1223,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
12141223

12151224
return 1;
12161225
}
1217-
#line 1218 "ext/standard/var_unserializer.c"
1226+
#line 1227 "ext/standard/var_unserializer.c"
12181227
yy95:
12191228
yych = *++YYCURSOR;
12201229
if (yych <= ',') {
@@ -1237,7 +1246,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
12371246
if (yych <= '9') goto yy97;
12381247
if (yych != ';') goto yy18;
12391248
++YYCURSOR;
1240-
#line 465 "ext/standard/var_unserializer.re"
1249+
#line 474 "ext/standard/var_unserializer.re"
12411250
{
12421251
long id;
12431252

@@ -1258,9 +1267,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
12581267

12591268
return 1;
12601269
}
1261-
#line 1262 "ext/standard/var_unserializer.c"
1270+
#line 1271 "ext/standard/var_unserializer.c"
12621271
}
1263-
#line 814 "ext/standard/var_unserializer.re"
1272+
#line 823 "ext/standard/var_unserializer.re"
12641273

12651274

12661275
return 0;

ext/standard/var_unserializer.re

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,12 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
400400

401401
(*p) += 2;
402402

403-
object_init_ex(*rval, ce);
403+
if (ce->unserialize == NULL) {
404+
object_init_ex(*rval, ce);
405+
} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) {
406+
return 0;
407+
}
408+
404409
return elements;
405410
}
406411

@@ -412,6 +417,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
412417
zval *retval_ptr = NULL;
413418
zval fname;
414419

420+
if (Z_TYPE_PP(rval) != IS_OBJECT) {
421+
return 0;
422+
}
423+
415424
if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) {
416425
return 0;
417426
}

0 commit comments

Comments
 (0)