diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 343b461..0c6777b 100644 --- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php @@ -137,10 +137,30 @@ protected function getLocalPath($uri = NULL) { $realpath = realpath(dirname($path)) . '/' . drupal_basename($path); } $directory = realpath($this->getDirectoryPath()); - if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) { + + // Get the target path relative to the files repository. + $target = DIRECTORY_SEPARATOR . $this->getTarget($uri); + // Get the files repository directory. + $repository = realpath($this->getDirectoryPath()); + // Get the target directory. + $target_dir = realpath(dirname($repository . $target)) . DIRECTORY_SEPARATOR; + // Get the target name, without any directory components. + $target_name = drupal_basename($repository . $target); + // A directory component can point outside its parent directory if the path + // separator ('/' or '\') is followed by '..' to reference the parent + // directory. + $pattern = '@(/|\\\\)\.\.@'; + // Check whether the target can possibly point outside its parent. + $traversal = preg_match($pattern, $target); + // Check whether the target dir exists within the files repository. + $subdirectory = strpos($target_dir, $repository . DIRECTORY_SEPARATOR) === 0; + if ($traversal && !$subdirectory) { + // If the target path contains directory-traversal parts such as '/..' and + // does not resolve to a subdirectory of the repository, then return FALSE + // to avoid a possible exploit. return FALSE; } - return $realpath; + return $target_dir . $target_name; } /**