Open source software security

Auditing Drupal Modules for Cross Site Scripting Vulnerabilities

25 March 2010

About XSS

Cross site scripting (XSS) is a pervasive problem in Drupal because the development team takes the approach that data should be sanitized upon display rather than input. The rational for this decision is to maintain data integrity despite translation or manipulation. This is a somewhat non-standard approach in web application circles and leads to no small amount of confusion about "trusted" data sources and the display of data. In general, all user supplied data must be filtered upon display. Drupal provides several useful API calls to facilitate this transformation. These include, but are not limited to, filter_xss(), check_plain(), and the t() function. Drupal output sanitization functions must be used carefully and properly, however, especially the t() function as misuse can introduce unexpected vulnerabilities.

Impact

Cross site scripting can have an incredibly damaging effect on a Drupal site. This is mainly due to the fact that a script can utilize AJAX and other functions to create a same domain request forgery. This is distinct from a cross site request forgery (XSRF) in that the user is actually logged into and browsing the domain. Because the Drupal user administration doesn't require re-entry of a password in order to implement changes, any JavaScript on a Drupal site can silently update a user's password if they request the JavaScript. This means that in addition to introducing malware, redirecting users, or other nefarious interactions, arbitrary script on a Drupal site can actually interact with the system allowing an XSS vulnerability to escalate into an account compromise or even a site compromise.

Types of XSS

There are generally two classifications of cross site scripting, stored and reflected. A stored cross site scripting vulnerability is one that persists on the site (usually in the database) so that every user requesting a page will be exposed to the attack. A reflected cross site scripting vulnerability is one that requires input from the requestor in order to induce the vulnerability. In a reflected XSS malicious users usually induce victims to click on or otherwise follow links that contain additional URL parameters. This type of attack can also be used against web crawlers, otherwise known as spiders. This is usually done to inject HTML links into page display if the crawler follows a certain link, thus causing the crawler to evaluate your Drupal site as though it contained a link to another site, usually to boost the other site's ranking or search relevance.

Examples

When reviewing code for XSS vulnerabilities you want to be sure to check for any occurrence of user supplied data that is being rendered. This not only includes obvious user supplied data such as form data or URL variables, but also less apparent user supplied data such as browser referer values, node titles, and usernames. Any data that users can maniuplate could become a target for script injection. It also bears mentioning that injections do not necessarily have to include the "" so that when alerts appear you can easily spot the offending form field. Of course, once an input is identified you have to move toward tracking down the source. Usually searching the source code for the help text or other display around the form field is useful. Drupal uses the form api to compose many of the forms so searching for specific display text is the key to finding the code that invokes a specific form field. Of course, most XSS actually manifests during display, so you should look outside of the form that actually allows for input of the data to the places where the XSS is rendered. Sometimes this can take a little digging. Additionally, there are some modules that allow for input of data but handle sanitization perfectly well and it is only with the addition of other modules or themes that problems creep up. This is especially true of XSS that you might find in a complex module such as Views, or Bibliography. This process can be quite time consuming, but extremely accurate. The Tag Order module is a great example of a module that requires a complex interaction of inputs to trigger a cross site scripting vulnerability as described in http://drupal.org/node/745386. By creating a new taxonomy with the name "" from the Administer -> Content management -> Taxonomy screen you exploit a stored cross site scripting vulnerability. This condition is triggered when someone goes to the administration screen for the Tag Order module at Administer -> Site configuration -> Tag Order Settings. Looking at this screen makes the vulnerability obvious, but finding it in code is a bit trickier. The simplest way is to search through the module code for the string "The entry order for these vocabularies should be remembered." We find this string in line 37 in the function:
function tagorder_admin_settings() {
  $vocabs = taxonomy_get_vocabularies();
  $vocabulary = array();
  foreach ($vocabs AS $vocab) {
    $vocabulary[$vocab->vid] = $vocab->name;
  }
  $form['tagorder_vocab'] = array(
    '#type' => 'checkboxes',
    '#title' => t('The entry order for these vocabularies should be remembered.'),
    '#options' => $vocabulary,
    '#default_value' => variable_get('tagorder_vocab', array('tags')),
  );
  $form['array_filter'] = array('#type' => 'hidden');
  return system_settings_form($form);
}
You can easily see that the $vocabulary variable isn't being sanitized on output. Simply surrounding the $vocab->name in a check_plain() mitigates this vulnerability. Referring to the module CVS documentation at http://drupalcode.org/viewvc/drupal/contributions/modules/tagorder/tagorder.module?r1=1.2&r2=1.2.2.1 you can clearly see that this is exactly what was done to fix this vulnerability.

Reporting Findings

Once you've identified an XSS in a Drupal module it is important to report the problem directly to the Drupal security team. Resist the temptation to report the problem directly to the module maintainer. The Drupal security team will take care of coordination with the maintainer and the release of a fix along with a security announcement on the main Drupal site. Including an exhaustive report of the problem, including a proof of concept, or path to exploitation, as well as a technical description of the problem including line numbers of code that causes problems, or even a patch, will greatly increase the responsiveness of the Drupal security team. Be prepared to get nothing from the team but a canned response and a ticket number. Sometimes it may take some prodding to get an actual response and commitment from the team but in general they are good about addressing issues. If you don't get a response follow up and include a timeline to disclosing the flaw. If you still don't get a response after a week or two then you may want to disclose the vulnerability publicly. The best way to do this is to report the problem on the module's bug forum. This insures that the module maintainer is aware of the problem and can work on a fix independent of the Drupal security team. After reporting to the module maintainer you may want to publish to a public mailing list such as Full-Disclosure in order to warn other Drupal site maintainers who may be using the module and who would want to know about the problems.