1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
From dda81f0505217a95db065e6bf9cc2d81eb902417 Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Tue, 4 Aug 2015 14:00:29 -0700
Subject: [PATCH] Fix bug #70019 - limit extracted files to given directory
---
ext/phar/phar_object.c | 50 +++++++++++++++++++++++++++++++++++++++----
ext/phar/tests/bug70019.phpt | 22 +++++++++++++++++++
ext/phar/tests/bug70019.zip | Bin 0 -> 184 bytes
3 files changed, 68 insertions(+), 4 deletions(-)
create mode 100644 ext/phar/tests/bug70019.phpt
create mode 100644 ext/phar/tests/bug70019.zip
Index: php5-5.3.3.1/ext/phar/phar_object.c
===================================================================
--- php5-5.3.3.1.orig/ext/phar/phar_object.c 2015-10-20 11:40:42.000000000 +0200
+++ php5-5.3.3.1/ext/phar/phar_object.c 2015-10-20 11:40:42.000000000 +0200
@@ -4204,6 +4204,9 @@
php_stream *fp;
char *fullpath, *slash;
mode_t mode;
+ cwd_state new_state;
+ char *filename;
+ size_t filename_len;
if (entry->is_mounted) {
/* silently ignore mounted entries */
@@ -4213,8 +4216,39 @@
if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) {
return SUCCESS;
}
+ /* strip .. from path and restrict it to be under dest directory */
+ new_state.cwd = (char*)malloc(2);
+ new_state.cwd[0] = DEFAULT_SLASH;
+ new_state.cwd[1] = '\0';
+ new_state.cwd_length = 1;
+ if (virtual_file_ex(&new_state, entry->filename, NULL, CWD_EXPAND TSRMLS_CC) != 0 ||
+ new_state.cwd_length <= 1) {
+ if (EINVAL == errno && entry->filename_len > 50) {
+ char *tmp = estrndup(entry->filename, 50);
+ spprintf(error, 4096, "Cannot extract \"%s...\" to \"%s...\", extracted filename is too long for filesystem", tmp, dest);
+ efree(tmp);
+ } else {
+ spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
+ }
+ free(new_state.cwd);
+ return FAILURE;
+ }
+ filename = new_state.cwd + 1;
+ filename_len = new_state.cwd_length - 1;
+#ifdef PHP_WIN32
+ /* unixify the path back, otherwise non zip formats might be broken */
+ {
+ int cnt = filename_len;
+
+ do {
+ if ('\\' == filename[cnt]) {
+ filename[cnt] = '/';
+ }
+ } while (cnt-- >= 0);
+ }
+#endif
- len = spprintf(&fullpath, 0, "%s/%s", dest, entry->filename);
+ len = spprintf(&fullpath, 0, "%s/%s", dest, filename);
if (len >= MAXPATHLEN) {
char *tmp;
@@ -4228,18 +4262,21 @@
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s...\", extracted filename is too long for filesystem", entry->filename, fullpath);
}
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
if (!len) {
spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename);
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
if (PHAR_OPENBASEDIR_CHECKPATH(fullpath)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
@@ -4247,14 +4284,15 @@
if (!overwrite && SUCCESS == php_stream_stat_path(fullpath, &ssb)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", path already exists", entry->filename, fullpath);
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
/* perform dirname */
- slash = zend_memrchr(entry->filename, '/', entry->filename_len);
+ slash = zend_memrchr(filename, '/', filename_len);
if (slash) {
- fullpath[dest_len + (slash - entry->filename) + 1] = '\0';
+ fullpath[dest_len + (slash - filename) + 1] = '\0';
} else {
fullpath[dest_len] = '\0';
}
@@ -4264,23 +4302,27 @@
if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK, PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
} else {
if (!php_stream_mkdir(fullpath, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
spprintf(error, 4096, "Cannot extract \"%s\", could not create directory \"%s\"", entry->filename, fullpath);
efree(fullpath);
+ free(new_state.cwd);
return FAILURE;
}
}
}
if (slash) {
- fullpath[dest_len + (slash - entry->filename) + 1] = '/';
+ fullpath[dest_len + (slash - filename) + 1] = '/';
} else {
fullpath[dest_len] = '/';
}
+ filename = NULL;
+ free(new_state.cwd);
/* it is a standalone directory, job done */
if (entry->is_dir) {
efree(fullpath);
|