From 8d87bb34f3c6103ab81e5342d8b3d297832d178a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 8 Nov 2017 11:01:16 +0100 Subject: [PATCH] Fix file disclosure vulnerability caused by insuficient input validation in relation with attachment plugins (#6026) --- .../database_attachments.php | 7 ++- .../filesystem_attachments.php | 47 +++++++++++++++++-- program/include/rcmail.php | 5 +- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/plugins/database_attachments/database_attachments.php b/plugins/database_attachments/database_attachments.php index 735915ae33d..ce2613942bb 100644 --- a/plugins/database_attachments/database_attachments.php +++ b/plugins/database_attachments/database_attachments.php @@ -67,6 +67,8 @@ function save($args) if ($args['data'] === false) { return $args; } + + $args['path'] = null; } $data = base64_encode($args['data']); @@ -113,10 +115,13 @@ function get($args) $cache = $this->get_cache(); $data = $cache->read($args['id']); - if ($data) { + if ($data !== null && $data !== false) { $args['data'] = base64_decode($data); $args['status'] = true; } + else { + $args['status'] = false; + } return $args; } diff --git a/plugins/filesystem_attachments/filesystem_attachments.php b/plugins/filesystem_attachments/filesystem_attachments.php index 50bd6246501..df6708b7829 100644 --- a/plugins/filesystem_attachments/filesystem_attachments.php +++ b/plugins/filesystem_attachments/filesystem_attachments.php @@ -7,12 +7,19 @@ * attachments of messages currently being composed, writing attachments * to disk when drafts with attachments are re-opened and writing * attachments to disk for inline display in current html compositions. + * It also handles uploaded files for other uses, so not only attachments. * * Developers may wish to extend this class when creating attachment * handler plugins: * require_once('plugins/filesystem_attachments/filesystem_attachments.php'); * class myCustom_attachments extends filesystem_attachments * + * Note for developers: It is plugin's responsibility to care about security. + * So, e.g. if the plugin is asked about some file path it should check + * if it's really the storage path of the plugin and not e.g. /etc/passwd. + * It is done by setting 'status' flag on every plugin hook it uses. + * Roundcube core will trust the returned path if status=true. + * * @license GNU GPLv3+ * @author Ziba Scott * @author Thomas Bruederli @@ -107,7 +114,7 @@ function save($args) */ function remove($args) { - $args['status'] = @unlink($args['path']); + $args['status'] = $this->verify_path($args['path']) && @unlink($args['path']); return $args; } @@ -118,7 +125,7 @@ function remove($args) */ function display($args) { - $args['status'] = file_exists($args['path']); + $args['status'] = $this->verify_path($args['path']) && file_exists($args['path']); return $args; } @@ -129,6 +136,10 @@ function display($args) */ function get($args) { + if (!$this->verify_path($args['path'])) { + $args['path'] = null; + } + return $args; } @@ -147,7 +158,7 @@ function cleanup($args) } foreach ((array)$files as $filename) { - if(file_exists($filename)) { + if (file_exists($filename)) { unlink($filename); } } @@ -182,4 +193,34 @@ private function find_file_by_id($id) } } } + + /** + * For security we'll always verify the file path stored in session, + * as session entries can be faked in various ways e.g. #6026. + * We allow only files in Roundcube temp dir + */ + protected function verify_path($path) + { + if (empty($path)) { + return false; + } + + $rcmail = rcube::get_instance(); + $temp_dir = $rcmail->config->get('temp_dir'); + $file_path = pathinfo($path, PATHINFO_DIRNAME); + + if ($temp_dir !== $file_path) { + rcube::raise_error(array( + 'code' => 403, + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => sprintf("%s can't read %s (not in temp_dir)", + $rcmail->get_user_name(), substr($path, 0, 512)) + ), true, false); + + return false; + } + + return true; + } } diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 1d4751ebf6b..7592891bc4f 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -640,8 +640,9 @@ function login($username, $pass, $host = null, $cookiecheck = false) $_SESSION['password'] = $this->encrypt($pass); $_SESSION['login_time'] = time(); - if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_') { - $_SESSION['timezone'] = rcube_utils::get_input_value('_timezone', rcube_utils::INPUT_GPC); + $timezone = rcube_utils::get_input_value('_timezone', rcube_utils::INPUT_GPC); + if ($timezone && is_string($timezone) && $timezone != '_default_') { + $_SESSION['timezone'] = $timezone; } // fix some old settings according to namespace prefix