Backported from 5.6.25 by Remi. From c1cfd6a9fe23765191ea2f654790c7b127d4b797 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 2 Aug 2016 01:08:42 -0700 Subject: [PATCH] Fix bug #72663 - destroy broken object when unserializing --- ext/standard/tests/strings/bug72663.phpt | 26 +++++++++++ ext/standard/tests/strings/bug72663_2.phpt | 17 ++++++++ ext/standard/var_unserializer.c | 70 ++++++++++++++++-------------- ext/standard/var_unserializer.re | 5 ++- 4 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 ext/standard/tests/strings/bug72663.phpt create mode 100644 ext/standard/tests/strings/bug72663_2.phpt diff --git a/ext/standard/tests/strings/bug72663.phpt b/ext/standard/tests/strings/bug72663.phpt new file mode 100644 index 0000000..e61f939 --- /dev/null +++ b/ext/standard/tests/strings/bug72663.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- +data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:4;}'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; + +$data = unserialize($exploit); +echo $data[1]; +?> +DONE +--EXPECTF-- +Notice: unserialize(): Unexpected end of serialized data in %sbug72663.php on line %d + +Notice: unserialize(): Error at offset 46 of 47 bytes in %sbug72663.php on line %d +DONE \ No newline at end of file diff --git a/ext/standard/tests/strings/bug72663_2.phpt b/ext/standard/tests/strings/bug72663_2.phpt new file mode 100644 index 0000000..ac605e9 --- /dev/null +++ b/ext/standard/tests/strings/bug72663_2.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- + +DONE +--EXPECTF-- +Notice: session_decode(): Unexpected end of serialized data in %sbug72663_2.php on line %d +array(0) { +} +DONE \ No newline at end of file diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 1e45b03..e4ddecf 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -437,6 +437,9 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) } if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { + /* We've got partially constructed object on our hands here. Wipe it */ + zend_hash_clean(Z_OBJPROP_PP(rval)); + ZVAL_NULL(*rval); return 0; } diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index d1d4ef9..c1c18c9 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -443,6 +443,9 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) } if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { + /* We've got partially constructed object on our hands here. Wipe it. */ + zend_hash_clean(Z_OBJPROP_PP(rval)); + ZVAL_NULL(*rval); return 0; } From b25f44098fdc8cecfd62d0fc5422c23d8747dcd2 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Thu, 4 Aug 2016 00:03:31 -0700 Subject: [PATCH] Update comment --- ext/standard/var_unserializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index e4ddecf..1d459ae 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -437,7 +437,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) } if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { - /* We've got partially constructed object on our hands here. Wipe it */ + /* We've got partially constructed object on our hands here. Wipe it. */ zend_hash_clean(Z_OBJPROP_PP(rval)); ZVAL_NULL(*rval); return 0; From 75c0dbdd028ffa20eae5cf3a2fae194961464b8b Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 7 Aug 2016 15:33:29 -0700 Subject: [PATCH] Improve fix for #72663 --- ext/standard/tests/strings/bug72663_3.phpt | 18 ++++++++ ext/standard/var_unserializer.c | 68 ++++++++++++++++-------------- ext/standard/var_unserializer.re | 8 +++- 3 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 ext/standard/tests/strings/bug72663_3.phpt diff --git a/ext/standard/tests/strings/bug72663_3.phpt b/ext/standard/tests/strings/bug72663_3.phpt new file mode 100644 index 0000000..e336bc8 --- /dev/null +++ b/ext/standard/tests/strings/bug72663_3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- +ryat = str_repeat('A', 0x112); + } +} + +$poc = 'O:8:"stdClass":1:{i:0;O:3:"obj":1:{s:4:"ryat";R:1;'; +unserialize($poc); +?> +DONE +--EXPECTF-- +Notice: unserialize(): Error at offset 51 of 50 bytes in %sbug72663_3.php on line %d +DONE \ No newline at end of file diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 1d459ae..c8e6f8a 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -438,11 +438,17 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { /* We've got partially constructed object on our hands here. Wipe it. */ - zend_hash_clean(Z_OBJPROP_PP(rval)); + if(Z_TYPE_PP(rval) == IS_OBJECT) { + zend_hash_clean(Z_OBJPROP_PP(rval)); + } ZVAL_NULL(*rval); return 0; } + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (Z_OBJCE_PP(rval) != PHP_IC_ENTRY && zend_hash_exists(&Z_OBJCE_PP(rval)->function_table, "__wakeup", sizeof("__wakeup"))) { INIT_PZVAL(&fname); diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index c1c18c9..11b93c5 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -444,11 +444,17 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { /* We've got partially constructed object on our hands here. Wipe it. */ - zend_hash_clean(Z_OBJPROP_PP(rval)); + if(Z_TYPE_PP(rval) == IS_OBJECT) { + zend_hash_clean(Z_OBJPROP_PP(rval)); + } ZVAL_NULL(*rval); return 0; } + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (Z_OBJCE_PP(rval) != PHP_IC_ENTRY && zend_hash_exists(&Z_OBJCE_PP(rval)->function_table, "__wakeup", sizeof("__wakeup"))) { INIT_PZVAL(&fname);