Skip to content

Commit

Permalink
Fix file disclosure vulnerability caused by insuficient input validat…
Browse files Browse the repository at this point in the history
…ion in relation with attachment plugins (#6026)
  • Loading branch information
alecpl committed Nov 8, 2017
1 parent ca74231 commit 8d87bb3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 6 deletions.
7 changes: 6 additions & 1 deletion plugins/database_attachments/database_attachments.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ function save($args)
if ($args['data'] === false) {
return $args;
}

$args['path'] = null;
}

$data = base64_encode($args['data']);
Expand Down Expand Up @@ -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;
}
Expand Down
47 changes: 44 additions & 3 deletions plugins/filesystem_attachments/filesystem_attachments.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <[email protected]>
* @author Thomas Bruederli <[email protected]>
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -129,6 +136,10 @@ function display($args)
*/
function get($args)
{
if (!$this->verify_path($args['path'])) {
$args['path'] = null;
}

return $args;
}

Expand All @@ -147,7 +158,7 @@ function cleanup($args)
}

foreach ((array)$files as $filename) {
if(file_exists($filename)) {
if (file_exists($filename)) {
unlink($filename);
}
}
Expand Down Expand Up @@ -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;
}
}
5 changes: 3 additions & 2 deletions program/include/rcmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8d87bb3

Please sign in to comment.