Drupal 6.22 Core XSS Vulnerability

25 August 2011
Drupal 6.22 core contains a cross site scripting vulnerability in the user module.

Description of Vulnerability:

Drupal (http://drupal.org) is a robust content management system (CMS) written in PHP and MySQL that provides extensibility through various core modules. The user module controls user login and management. The user module's access rules functionality contains a persistent cross site scripting vulnerability because it fails to sanitize mask values before display.

Systems affected:

Drupal 6.22 was tested and shown to be vulnerable.

Impact:

XSS vulnerabilities may expose site administrative accounts to compromise which could lead to web server process compromise.

Mitigating factors:

Attackers must have the "administer permissions" permission, which is an administrative permission that exposes several dangerous operations. As such Drupal security has stated that they don't consider XSS that require this permission to be a security issue (http://drupal.org/security-advisory-policy).

Proof of Concept:

  1. Install Drupal 6.22
  2. Navigate to Administer -> User management -> Access rules (?q=/admin/user/rules)
  3. Click the 'Add rule' tab
  4. Enter "<script>alert('xss');</script>" in the "Mask" text area and click the 'Add rule' button
  5. JavaScript will now be rendered for each page view of ?q=/amin/user/rules

Patch

Applying the following patch resolves this issue

$ diff -up user.admin.inc.orig user.admin.inc 
--- user.admin.inc.orig	2011-08-19 13:03:45.000000000 -0700
+++ user.admin.inc	2011-08-19 13:03:49.000000000 -0700
@@ -895,7 +895,7 @@ function user_admin_access() {
   $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host'));
   $rows = array();
   while ($rule = db_fetch_object($result)) {
-    $rows[] = array($rule->status ? t('allow') : t('deny'), $access_types[$rule->type], $rule->mask, l(t('edit'), 'admin/user/rules/edit/'. $rule->aid), l(t('delete'), 'admin/user/rules/delete/'. $rule->aid));
+    $rows[] = array($rule->status ? t('allow') : t('deny'), $access_types[$rule->type], check_plain($rule->mask), l(t('edit'), 'admin/user/rules/edit/'. $rule->aid), l(t('delete'), 'admin/user/rules/delete/'. $rule->aid));
   }
   if (empty($rows)) {
     $rows[] = array(array('data' => '<em>'. t('There are currently no access rules.') .'</em>', 'colspan' => 5));