Merged 7.96.

merge-requests/2943/head
xjm 2023-04-19 11:14:59 -05:00
commit e11e3f4f7f
No known key found for this signature in database
GPG Key ID: 206B0B8743BDF4C2
5 changed files with 115 additions and 2 deletions

View File

@ -1,6 +1,11 @@
Drupal 7.xx, xxxx-xx-xx (development version)
-----------------------
Drupal 7.96, 2023-04-19
-----------------------
- Fixed security issues:
- SA-CORE-2023-005
Drupal 7.95, 2023-03-15
-----------------------
- Fixed security issues:

View File

@ -2073,6 +2073,7 @@ function file_download() {
$scheme = array_shift($args);
$target = implode('/', $args);
$uri = $scheme . '://' . $target;
$uri = file_uri_normalize_dot_segments($uri);
if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {
$headers = file_download_headers($uri);
if (count($headers)) {
@ -2730,6 +2731,58 @@ function file_get_content_headers($file) {
);
}
/**
* Normalize dot segments in a URI.
*
* @param $uri
* A stream, referenced as "scheme://target".
*
* @return string
* The URI with dot segments removed and slashes as directory separator.
*/
function file_uri_normalize_dot_segments($uri) {
$scheme = file_uri_scheme($uri);
if (file_stream_wrapper_valid_scheme($scheme)) {
$target = file_uri_target($uri);
if ($target !== FALSE) {
if (!in_array($scheme, variable_get('file_sa_core_2023_005_schemes', array()))) {
$class = file_stream_wrapper_get_class($scheme);
$is_local = is_subclass_of($class, DrupalLocalStreamWrapper::class);
if ($is_local) {
$target = str_replace(DIRECTORY_SEPARATOR, '/', $target);
}
$parts = explode('/', $target);
$normalized_parts = array();
while ($parts) {
$part = array_shift($parts);
if ($part === '' || $part === '.') {
continue;
}
elseif ($part === '..' && $is_local && $normalized_parts === array()) {
$normalized_parts[] = $part;
break;
}
elseif ($part === '..') {
array_pop($normalized_parts);
}
else {
$normalized_parts[] = $part;
}
}
$target = implode('/', array_merge($normalized_parts, $parts));
}
$uri = $scheme . '://' . $target;
}
}
return $uri;
}
/**
* @} End of "defgroup file".
*/

View File

@ -819,6 +819,20 @@ function image_style_deliver($style, $scheme = NULL) {
array_shift($args);
array_shift($args);
$target = implode('/', $args);
$image_uri = $scheme . '://' . $target;
$image_uri = file_uri_normalize_dot_segments($image_uri);
if (file_stream_wrapper_valid_scheme($scheme)) {
$normalized_target = file_uri_target($image_uri);
if ($normalized_target !== FALSE) {
if (!in_array($scheme, variable_get('file_sa_core_2023_005_schemes', array()))) {
$parts = explode('/', $normalized_target);
if (array_intersect($parts, array('.', '..'))) {
return MENU_NOT_FOUND;
}
}
}
}
// Check that the style is defined, the scheme is valid.
$valid = !empty($style) && !empty($scheme) && file_stream_wrapper_valid_scheme($scheme);
@ -830,7 +844,9 @@ function image_style_deliver($style, $scheme = NULL) {
// variable from leaving the site vulnerable to the most serious attacks, a
// token is always required when a derivative of a derivative is requested.)
$token = isset($_GET[IMAGE_DERIVATIVE_TOKEN]) ? $_GET[IMAGE_DERIVATIVE_TOKEN] : '';
$token_is_valid = $token === image_style_path_token($style['name'], $scheme . '://' . $target);
$token_is_valid =
$token === image_style_path_token($style['name'], $image_uri)
|| $token === image_style_path_token($style['name'], $scheme . '://' . $target);
if (!variable_get('image_allow_insecure_derivatives', FALSE) || strpos(ltrim($target, '\/'), 'styles/') === 0) {
$valid = $valid && $token_is_valid;
}
@ -838,7 +854,6 @@ function image_style_deliver($style, $scheme = NULL) {
return MENU_ACCESS_DENIED;
}
$image_uri = $scheme . '://' . $target;
$derivative_uri = image_style_path($style['name'], $image_uri);
$derivative_scheme = file_uri_scheme($derivative_uri);
@ -1084,9 +1099,11 @@ function image_style_flush($style) {
*/
function image_style_url($style_name, $path) {
$uri = image_style_path($style_name, $path);
$uri = file_uri_normalize_dot_segments($uri);
// The passed-in $path variable can be either a relative path or a full URI.
$original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path);
$original_uri = file_uri_normalize_dot_segments($original_uri);
// The token query is added even if the 'image_allow_insecure_derivatives'
// variable is TRUE, so that the emitted links remain valid if it is changed

View File

@ -4093,6 +4093,25 @@ function system_admin_paths() {
* Implements hook_file_download().
*/
function system_file_download($uri) {
$scheme = file_uri_scheme($uri);
if (file_stream_wrapper_valid_scheme($scheme)) {
$target = file_uri_target($uri);
if ($target !== FALSE) {
if (!in_array($scheme, variable_get('file_sa_core_2023_005_schemes', array()))) {
if (DIRECTORY_SEPARATOR !== '/') {
$class = file_stream_wrapper_get_class($scheme);
if (is_subclass_of($class, DrupalLocalStreamWrapper::class)) {
$target = str_replace(DIRECTORY_SEPARATOR, '/', $target);
}
}
$parts = explode('/', $target);
if (array_intersect($parts, array('.', '..'))) {
return -1;
}
}
}
}
$core_schemes = array('public', 'private', 'temporary');
$additional_public_schemes = array_diff(variable_get('file_additional_public_schemes', array()), $core_schemes);
if ($additional_public_schemes) {

View File

@ -818,3 +818,22 @@ $conf['mail_display_name_site_name'] = TRUE;
* Use this variable to set a custom cron lock expiration timeout (float).
*/
# $conf['cron_lock_expiration_timeout'] = 900.0;
/**
* File schemes whose paths should not be normalized:
*
* Normally, Drupal normalizes '/./' and '/../' segments in file URIs in order
* to prevent unintended file access. For example, 'private://css/../image.png'
* is normalized to 'private://image.png' before checking access to the file.
*
* On Windows, Drupal also replaces '\' with '/' in URIs for the local
* filesystem.
*
* If file URIs with one or more scheme should not be normalized like this, then
* list the schemes here. For example, if 'porcelain://china/./plate.png' should
* not be normalized to 'porcelain://china/plate.png', then add 'porcelain' to
* this array. In this case, make sure that the module providing the 'porcelain'
* scheme does not allow unintended file access when using '/../' to move up the
* directory tree.
*/
# $conf['file_sa_core_2023_005_schemes'] = array('porcelain');