|
1 CVE-2015-2783 |
|
2 Community BUG: |
|
3 https://bugs.php.net/bug.php?id=69324 |
|
4 Community CODE: |
|
5 http://git.php.net/?p=php-src.git;a=commit;h=9faaee66fa493372c7340b1ab05f8fd115131a42 |
|
6 Below is the community patch. |
|
7 |
|
8 Not including the test files at the moment: |
|
9 ext/phar/tests/bug69324.phar |
|
10 ext/phar/tests/bug69324.phpt |
|
11 because our version of gpatch doesn't understand the git binary data file. |
|
12 |
|
13 |
|
14 |
|
15 From 9faaee66fa493372c7340b1ab05f8fd115131a42 Mon Sep 17 00:00:00 2001 |
|
16 From: Stanislav Malyshev <[email protected]> |
|
17 Date: Sun, 5 Apr 2015 15:07:36 -0700 |
|
18 Subject: [PATCH] Fixed bug #69324 (Buffer Over-read in unserialize when |
|
19 parsing Phar) |
|
20 |
|
21 --- |
|
22 ext/phar/phar.c | 65 ++++++++++++++++++++----------------------- |
|
23 ext/phar/phar_internal.h | 2 +- |
|
24 ext/phar/tests/bug69324.phar | Bin 0 -> 269 bytes |
|
25 ext/phar/tests/bug69324.phpt | 17 +++++++++++ |
|
26 4 files changed, 48 insertions(+), 36 deletions(-) |
|
27 create mode 100644 ext/phar/tests/bug69324.phar |
|
28 create mode 100644 ext/phar/tests/bug69324.phpt |
|
29 |
|
30 diff --git a/ext/phar/phar.c b/ext/phar/phar.c |
|
31 index ec82351..bf0c985 100644 |
|
32 --- a/ext/phar/phar.c |
|
33 +++ b/ext/phar/phar.c |
|
34 @@ -603,25 +603,18 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len |
|
35 * |
|
36 * data is the serialized zval |
|
37 */ |
|
38 -int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSRMLS_DC) /* {{{ */ |
|
39 +int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_len TSRMLS_DC) /* {{{ */ |
|
40 { |
|
41 const unsigned char *p; |
|
42 - php_uint32 buf_len; |
|
43 php_unserialize_data_t var_hash; |
|
44 |
|
45 - if (!zip_metadata_len) { |
|
46 - PHAR_GET_32(*buffer, buf_len); |
|
47 - } else { |
|
48 - buf_len = zip_metadata_len; |
|
49 - } |
|
50 - |
|
51 - if (buf_len) { |
|
52 + if (zip_metadata_len) { |
|
53 ALLOC_ZVAL(*metadata); |
|
54 INIT_ZVAL(**metadata); |
|
55 p = (const unsigned char*) *buffer; |
|
56 PHP_VAR_UNSERIALIZE_INIT(var_hash); |
|
57 |
|
58 - if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) { |
|
59 + if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash TSRMLS_CC)) { |
|
60 PHP_VAR_UNSERIALIZE_DESTROY(var_hash); |
|
61 zval_ptr_dtor(metadata); |
|
62 *metadata = NULL; |
|
63 @@ -633,19 +626,14 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR |
|
64 if (PHAR_G(persist)) { |
|
65 /* lazy init metadata */ |
|
66 zval_ptr_dtor(metadata); |
|
67 - *metadata = (zval *) pemalloc(buf_len, 1); |
|
68 - memcpy(*metadata, *buffer, buf_len); |
|
69 - *buffer += buf_len; |
|
70 + *metadata = (zval *) pemalloc(zip_metadata_len, 1); |
|
71 + memcpy(*metadata, *buffer, zip_metadata_len); |
|
72 return SUCCESS; |
|
73 } |
|
74 } else { |
|
75 *metadata = NULL; |
|
76 } |
|
77 |
|
78 - if (!zip_metadata_len) { |
|
79 - *buffer += buf_len; |
|
80 - } |
|
81 - |
|
82 return SUCCESS; |
|
83 } |
|
84 /* }}}*/ |
|
85 @@ -666,6 +654,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char |
|
86 phar_entry_info entry; |
|
87 php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags; |
|
88 php_uint16 manifest_ver; |
|
89 + php_uint32 len; |
|
90 long offset; |
|
91 int sig_len, register_alias = 0, temp_alias = 0; |
|
92 char *signature = NULL; |
|
93 @@ -1031,16 +1020,21 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char |
|
94 mydata->is_persistent = PHAR_G(persist); |
|
95 |
|
96 /* check whether we have meta data, zero check works regardless of byte order */ |
|
97 + PHAR_GET_32(buffer, len); |
|
98 if (mydata->is_persistent) { |
|
99 - PHAR_GET_32(buffer, mydata->metadata_len); |
|
100 - if (phar_parse_metadata(&buffer, &mydata->metadata, mydata->metadata_len TSRMLS_CC) == FAILURE) { |
|
101 - MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); |
|
102 - } |
|
103 - } else { |
|
104 - if (phar_parse_metadata(&buffer, &mydata->metadata, 0 TSRMLS_CC) == FAILURE) { |
|
105 - MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); |
|
106 + mydata->metadata_len = len; |
|
107 + if(!len) { |
|
108 + /* FIXME: not sure why this is needed but removing it breaks tests */ |
|
109 + PHAR_GET_32(buffer, len); |
|
110 } |
|
111 } |
|
112 + if(len > endbuffer - buffer) { |
|
113 + MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)"); |
|
114 + } |
|
115 + if (phar_parse_metadata(&buffer, &mydata->metadata, len TSRMLS_CC) == FAILURE) { |
|
116 + MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); |
|
117 + } |
|
118 + buffer += len; |
|
119 |
|
120 /* set up our manifest */ |
|
121 zend_hash_init(&mydata->manifest, manifest_count, |
|
122 @@ -1075,7 +1069,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char |
|
123 entry.manifest_pos = manifest_index; |
|
124 } |
|
125 |
|
126 - if (buffer + entry.filename_len + 20 > endbuffer) { |
|
127 + if (entry.filename_len + 20 > endbuffer - buffer) { |
|
128 MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); |
|
129 } |
|
130 |
|
131 @@ -1111,19 +1105,20 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char |
|
132 entry.flags |= PHAR_ENT_PERM_DEF_DIR; |
|
133 } |
|
134 |
|
135 + PHAR_GET_32(buffer, len); |
|
136 if (entry.is_persistent) { |
|
137 - PHAR_GET_32(buffer, entry.metadata_len); |
|
138 - if (!entry.metadata_len) buffer -= 4; |
|
139 - if (phar_parse_metadata(&buffer, &entry.metadata, entry.metadata_len TSRMLS_CC) == FAILURE) { |
|
140 - pefree(entry.filename, entry.is_persistent); |
|
141 - MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); |
|
142 - } |
|
143 + entry.metadata_len = len; |
|
144 } else { |
|
145 - if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) { |
|
146 - pefree(entry.filename, entry.is_persistent); |
|
147 - MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); |
|
148 - } |
|
149 + entry.metadata_len = 0; |
|
150 + } |
|
151 + if (len > endbuffer - buffer) { |
|
152 + MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); |
|
153 + } |
|
154 + if (phar_parse_metadata(&buffer, &entry.metadata, len TSRMLS_CC) == FAILURE) { |
|
155 + pefree(entry.filename, entry.is_persistent); |
|
156 + MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); |
|
157 } |
|
158 + buffer += len; |
|
159 |
|
160 entry.offset = entry.offset_abs = offset; |
|
161 offset += entry.compressed_filesize; |
|
162 diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h |
|
163 index c9306c1..fcfc864 100644 |
|
164 --- a/ext/phar/phar_internal.h |
|
165 +++ b/ext/phar/phar_internal.h |
|
166 @@ -654,7 +654,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, |
|
167 char *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar TSRMLS_DC); |
|
168 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC); |
|
169 phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC); |
|
170 -int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSRMLS_DC); |
|
171 +int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_len TSRMLS_DC); |
|
172 void destroy_phar_manifest_entry(void *pDest); |
|
173 int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position, int follow_links TSRMLS_DC); |
|
174 php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC); |