In SA-CONTRIB-2010-066 two different XSS vulnerabilities were closed related to the file names of uploaded files.

It's possible that one or both are present in Drupal 7 core due to the FileField being brought into core.

the patch for FileField:

Index: filefield.token.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/filefield/filefield.token.inc,v
retrieving revision 1.5
diff -u -p -r1.5 filefield.token.inc
--- filefield.token.inc	30 Jul 2009 16:47:43 -0000	1.5
+++ filefield.token.inc	16 Jun 2010 20:43:38 -0000
@@ -38,9 +38,9 @@ function filefield_token_values($type, $
   if ($type == 'field' && isset($object[0]['fid'])) {
     $item = $object[0];
     $tokens['filefield-fid']                = $item['fid'];
-    $tokens['filefield-description']        = isset($item['data']['description']) ? $item['data']['description'] : '';
-    $tokens['filefield-filename']           = $item['filename'];
-    $tokens['filefield-filepath']           = $item['filepath'];
+    $tokens['filefield-description']        = isset($item['data']['description']) ? check_plain($item['data']['description']) : '';
+    $tokens['filefield-filename']           = check_plain($item['filename']);
+    $tokens['filefield-filepath']           = check_plain($item['filepath']);
     $tokens['filefield-filemime']           = $item['filemime'];
     $tokens['filefield-filesize']           = $item['filesize'];
     $tokens['filefield-filesize_formatted'] = format_size($item['filesize']);
Index: filefield_formatter.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/filefield/filefield_formatter.inc,v
retrieving revision 1.16
diff -u -p -r1.16 filefield_formatter.inc
--- filefield_formatter.inc	24 Apr 2010 03:54:59 -0000	1.16
+++ filefield_formatter.inc	16 Jun 2010 20:43:38 -0000
@@ -39,8 +39,7 @@ function theme_filefield_formatter_path_
   if (empty($item['filepath']) && !empty($item['fid'])) {
     $item = array_merge($item, field_file_load($item['fid']));
   }
-
-  return empty($item['filepath']) ? '' : file_create_path($item['filepath']);
+  return empty($item['filepath']) ? '' : check_plain(file_create_path($item['filepath']));
 }
 
 /**
@@ -63,7 +62,21 @@ function theme_filefield_formatter_url_p
     $item = array_merge($item, field_file_load($item['fid']));
   }
 
-  return empty($item['filepath']) ? '' : file_create_url($item['filepath']);
+  if (empty($item['filepath'])) {
+    return '';
+  }
+
+  // Encode the parts of the path to ensure URLs operate within href attributes.
+  // Private file paths are urlencoded for us inside of file_create_url().
+  if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) {
+    $parts = explode('/', $item['filepath']);
+    foreach ($parts as $index => $part) {
+      $parts[$index] = rawurlencode($part);
+    }
+    $item['filepath'] = implode('/', $parts);
+  }
+
+  return file_create_url($item['filepath']);
 }

Comments

rszrama’s picture

Status: Patch (to be ported) » Active

Reading over the File token integration in system.tokens.inc shows the tokens have been sanitized where requested.

Regarding the second chunk, in function file_field_formatter_view() of file.field.inc in D7:

    case 'file_url_plain':
      foreach ($items as $delta => $item) {
        $element[$delta] = array('#markup' => empty($item['uri']) ? '' : file_create_url($item['uri']));
      }
      break;

This seems related to the fix applied in SA-CONTRIB-2010-066, so I'm guessing file_create_url($item['uri']) needs to be wrapped in check_plain()?

I'm not actually sure what to make of the third chunk, as I don't see relevant code in the current File module.

int’s picture

Issue tags: +beta blocker

add beta blocker tag

philbar’s picture

This module does not appear on the D7 beta blocker section of the community initiative page. Should it be?

rszrama’s picture

Added it there.

pwolanin’s picture

After struggling with this issue for D6- the file path and file URL have to be treated quite differently. We have to insure the URL can be used in a web address, while the path is not guaranteed to be usable that way.

For this reason, we don't apply check_plain() to file_create_url(), but instead do the urlencode dance.

tstoeckler’s picture

StatusFileSize
new1.23 KB

This is a first stab at fixing file_field_formatter_view(). This is the first time I've ever been involved with stream wrappers / file API / file.module and in addition to the code being extremely ugly, I'm pretty certain the approach is also flawed. But now you experts have a patch to review and can tell me how to do it better/right. :)

tstoeckler’s picture

Status: Active » Needs work
int’s picture

Status: Needs work » Needs review

"But now you experts have a patch to review.."

dhthwy’s picture

Status: Needs review » Needs work

after thorough testing, AFAICT Drupal 7 core is not affected, so no patch is necessary.

File field's hook implementation of hook_field_formatter_view() calls file_create_url() for the 'URL to file' format, the other formats ('generic file' and 'table of files') use hook theme_file_link() which calls file_create_url() and l(). l() also check_plain()s the file's description (link anchor text) when descriptions are used for the 'generic file' and 'table of files' formats (the 'URL to file' format is plain text and therefore doesn't use a description).

file_create_url() URL encodes public and private file schemes via the call to $wrapper->getExternalUrl():

In file_create_url():

 if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
      return $wrapper->getExternalUrl();
    }
 

For the public stream wrapper this invokes:

 function getExternalUrl() {
    $path = str_replace('\\', '/', $this->getTarget());
    return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path);
  }
 

For the private stream wrapper this invokes:

 function getExternalUrl() {
    $path = str_replace('\\', '/', $this->getTarget());
    return url('system/files/' . $path, array('absolute' => TRUE));
  }
 

pwolanin, penny for your thoughts?

dhthwy’s picture

Status: Needs work » Needs review
pwolanin’s picture

Status: Needs review » Active

So, looking at the token code, seems like file module doesn't provide any(!) tokens in Drupal 7 core, so we don't need to worry about that part (for core at least).

In http://api.drupal.org/api/function/file_create_url/7

it looks like there could be a vulnerability for relative URLs or http/https URLs.

Also, in terms of the token values, it seems like we might actually get double encoding in some cases

http://api.drupal.org/api/function/system_tokens/7

So far I agree with the comment above that this is likely not an issue in Drupal 7 core.

dhthwy’s picture

pwolanin, I think you're right about http/https/relative URLs, but whose responsibility is it to url encode those? It seems like it would be the callers.

damien tournoud’s picture

Status: Active » Needs work
   case 'file_url_plain':
      foreach ($items as $delta => $item) {
        $element[$delta] = array('#markup' => empty($item['uri']) ? '' : file_create_url($item['uri']));
      }
      break;

This definitely needs a check_plain().

pwolanin’s picture

Status: Needs work » Active

@dhtwy - well I feel like we need to have a consistent expectation. If file_create_url encodes some URLs, then I think it should encode all of them?

pwolanin’s picture

I think calling url() inside system_tokens() is wrong.

dhthwy’s picture

pwolanin, yea, you're probably right.

damien tournoud’s picture

All those url() in system_tokens() needs to be wrapped in check_plain(), or they are not safe to output in an HTML context.

pwolanin’s picture

        case 'path':
          $replacements[$original] = $sanitize ? filter_xss($file->uri) : $file->uri;

should probably be:

        case 'path':
          $replacements[$original] = $sanitize ? check_plain($file->uri) : $file->uri;

pwolanin’s picture

So imagining we have a URL as the file URL. Do we require that to be URL encoded in the DB? I'm not sure you can do this anywhere in Drupal core, but we need to think about "interesting" contrib applications.

pwolanin’s picture

Status: Active » Needs work
StatusFileSize
new3.76 KB

This patch is a starting point, but wrong for file_create_url(). We don't actually want to use drupal_encode_path() on the full uri, since it may contain a query string.

E.g. imagine that the actual name of the file is passed to a CDN in the query string, or we are using something like s3 where we include an access token in the query string.

It seems like we need to build a new helper function to encode an arbitrary URL?

damien tournoud’s picture

Status: Needs work » Active

This is not a question of urlencoding, but of HTML encoding. Even a properly encoded URL needs to be HTML encoded before being output. system_token() is way too late in the process to worry about URL-encoding.

pwolanin’s picture

Status: Active » Needs review
StatusFileSize
new3.46 KB

hopefully this is more along the right track.

pwolanin’s picture

StatusFileSize
new3.46 KB

oops - uploaded one with a bad date token.

Status: Needs review » Needs work

The last submitted patch, 829822-tokens-filepaths-23.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
StatusFileSize
new6.4 KB

This patch removes the failing tests - I think they were invalid since there is no indication in the API that I should be able to add a file with query parameters.

damien tournoud’s picture

         case 'raw':
-          $replacements[$original] = filter_xss($date);
+          $replacements[$original] = $sanitize ? check_plain($date) : $date;
           break;

If this is a date from format_date(), it's already HTML, so filter_xss() was correct.

This patch removes the failing tests - I think they were invalid since there is no indication in the API that I should be able to add a file with query parameters.

We probably just need to make those tests generate absolute URLs for the test CSS and JS files with query strings, right?

pwolanin’s picture

One is supposed to be passing file names into drupal_add_js and drupal_add_css. As we discussed in IRC, there are cases where we cannot know whether ? and & is indicating a query string or is part of a path or file name.

In this case, FILE names (on disk) cannot meaningfully have query strings, so I think encoding them is correct.

damien tournoud’s picture

We have an 'external' option to drupal_add_js() and drupal_add_css(). I think that's what the test wanted to confirm.

pwolanin’s picture

@Damien - ok, then we need to rework the test.

pwolanin’s picture

Status: Needs review » Needs work

CNW for test fix ups.

pwolanin’s picture

patch doesn't apply cleanly

pwolanin’s picture

Status: Needs work » Needs review
StatusFileSize
new5.92 KB

Now with fixed tests.

scor’s picture

DamZ's comment in #26 still need to be addressed

pwolanin’s picture

@scor - $date is a unix timestamp (though can possibly be user input).

    if (empty($data['date'])) {
      $date = REQUEST_TIME;
    }
    else {
      $date = $data['date'];
    }
scor’s picture

Status: Needs review » Needs work

@damZ: $date comes from the snippet above posted by pwolanin, i.e. not coming from format_date().

scor’s picture

Status: Needs work » Reviewed & tested by the community

with DamZ's comments addressed, I meant to RTBC this.

dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Yay. :)

Status: Fixed » Closed (fixed)
Issue tags: -Security Advisory follow-up, -beta blocker

Automatically closed -- issue fixed for 2 weeks with no activity.