Drupal Less CSS Module XSS Vulnerability

23 May 2012
Reported: March 14, 2012

Description of Vulnerability:

Drupal (http://drupal.org) is a robust content management system (CMS) written in PHP and MySQL. The Drupal LESS CSS Preprocessor (hereafter Less) module (https://drupal.org/project/less) "will automatically process any LESS files that are added using drupal_add_css or added through your theme's .info file." The Less module contains a persistent cross site scripting (XSS) vulnerability due to the fact that it fails to sanitize Less error messages before display.

Systems affected:

Drupal 6.24 with Less 6.x-2.8 was tested and shown to be vulnerable.

Impact

Files with the .less extension in the Drupal module search path could be used to inject arbitrary HTML into site pages, potentially affecting all users . This could lead to privilege escalation, account compromise, or other attacks (such as client side exploits).

Mitigating factors:

In order to execute arbitrary script injection malicious users must be able to create or modify files on the filesystem, or trick someone into putting malicious files on the filesystem (such as hiding them in components of a seemingly legitimate Drupal module). Note that modification of Drupal's error display settings do not mitigate this vulnerability.

Proof of Concept Exploit:

  1. Install and enable the Less module
  2. Install another contrib module, such as admin_menu
  3. In sites/all/modules/admin_menu create a file named "admin_menu.php%00.less"
  4. Edit this file to contain the text "<?php phpinfo(); ?> <script>alert('xss');</script>"
  5. Save the file and clear caches to view the rendered JavaScript alert

Patch:

The following patches mitigates these vulnerabilities:
--- less.module	2012-03-14 08:06:35.973965077 -0400
+++ less.module	2012-03-14 08:06:54.614121537 -0400
@@ -110,7 +110,7 @@ function _less_build(&$vars, $hook) {
               file_save_data($output_data, $output_file, FILE_EXISTS_REPLACE);
             }
             catch (Exception $e) {
-              $message = 'LESS ERROR: '. $e->getMessage() .', '. $input_file;
+              $message = filter_xss('LESS ERROR: '. $e->getMessage() .', '. $input_file);
               watchdog('LESS', $message, array(), WATCHDOG_ERROR);
               if (user_access(LESS_PERMISSION)) {
                 drupal_set_message($message, 'error');