Checking for mailboxes with weak passwords

Stefaans

Well-Known Member
Mar 5, 2002
460
3
318
Vancouver, Canada
In recent weeks our servers have been under constant attack from spammers that sniff out mailboxes with weak passwords. It is amazing to see how often the password for [email protected] is merely "info" or even "password" :eek:

Nowadays WHM has an option to set a password policy. Years ago, however, there was no such feature and users could set up mailboxes with weak passwords. We needed a way to check older mailboxes for weak passwords.

I wrote the PHP script below to check all mailboxes on a server for weak passwords. The script checks the following passwords:
* The username, e.g. "info" for the mailbox [email protected].
* The domain, e.g. "domain" for the mailbox [email protected].
* A configurable list of common passwords. I took the top five that were published for 2011.

The script is made to be run from the shell as root:
Code:
php checkmailboxes.php
Modify the $conf variable for a options. By default the script echoes its basic progress on the screen; if you enable the 'debug' configuration variable, more in-depth information is displayed. The script also writes a list of vulnerable mailboxes found in a log file weakmailboxes.log.

There is obviously a lot more one could add to this script (e.g. automatically set a new password for each vulnerable mailbox, send the account owner a notification email etc.) but I am not going to bother at this time.

Code:
<?php
/**
 * Find mailboxes with weak passwords
 * Created by Stephen @ ANNO.COM
 *
 * Revisions:
 * ---------
 *
 * 2012/08/10
 *  Initial release.
 *
 */

$conf = array(
  'users_exclude'   => array('virtfs', 'cpanel_src', 'lost+found', 'cpanel_logs', 'cpeasyapache', 'apache_logs', 'logs', 'MySQL-install'),
  // Directories in /home that are not real users
  'domains_exclude' => array('cur', 'new', 'tmp', 'courierimaphieracl', 'courierimapkeywords'), // System directories in mail that are not domains
  'mailboxes_exclude' => array('cur', 'new', 'tmp', 'courierimapkeywords'), // System directories in domain directory that are not mailboxes
  'check_common_passwords' => TRUE,   // Set to TRUE to check the following passwords as well
  'common_passwords' => array('password', '123456', '12345678', 'qwerty', 'abc123'), // Allegedly the most popular passwords in 2011
  'debug' => FALSE, // Set to TRUE to display more verbose output
);

$mailboxes = array(); // Array of mailboxes
$weak_mailboxes_found = FALSE;

// IMAP config for fastest possible connection
imap_timeout(IMAP_OPENTIMEOUT, 1);
imap_timeout(IMAP_READTIMEOUT, 1);

// Create log file
$log = fopen('weakmailboxes.log', 'w');
fwrite($log, "List of mailboxes with weak passwords\n");
fwrite($log, 'Checks ' . ($conf['check_common_passwords'] ? '' : 'do not ') . "include common passwords\n");
fwrite($log, 'Report on ' . date('Y-m-d H:i') . "\n");
fwrite($log, "----------------------------------------------------------------------\n");

// Start screen output
echo "\n\n----------------------------------------------------------------------\n";
echo "Weak mailbox password check\n";

// Loop through cPanel users in home directory
if ($h_home = opendir('/home')) {
  echo "Building list of mailboxes:\n";
  if ($conf['debug']) {
    echo "(Directory handle: $h_home)\n";
  }
  while (FALSE !== ($user = readdir($h_home))) {
    if (is_dir("/home/$user/mail") && !in_array($user, $conf['users_exclude']) && (strpos($user, '.') === FALSE)) {
      $users[] = $user;

// Loop through user's domains
      echo "Looping through domains for user $user...\n";
      if ($h_user = opendir("/home/$user/mail")) {
        if ($conf['debug']) {
          echo "\t(User handle: $h_user)\n";
        }
        while (FALSE !== ($domain = readdir($h_user))) {
          if (is_dir("/home/$user/mail/$domain") && !in_array($domain, $conf['domains_exclude']) && ($domain[0] != '.')) {
            $domains[] = $domain;

// Loop through domain's mailboxes
            echo "\tLooping through mailboxes for domain $domain...\n";
            if ($h_domain = opendir("/home/$user/mail/$domain")) {
              if ($conf['debug']) {
                echo "\t(Domain handle: $h_domain)\n";
              }
              while (FALSE !== ($mailbox = readdir($h_domain))) {
                if (is_dir("/home/$user/mail/$domain/$mailbox") && !in_array($mailbox, $conf['mailboxes_exclude']) && ($mailbox[0] != '.')) {
                  $mailboxes["$domain with user $mailbox"] = array(
                    'user' => $mailbox,
                    'domain' => $domain,
                  );
                  echo "\t\[email protected]$domain\n";
                } // End if value mailbox
                else {
                  if (!in_array($domain, array('.', '..'))) {
                    if ($conf['debug']) {
                      echo "\t\tSkipping $mailbox\n";
                    }
                  }
                }
              } // End while loop for mailboxes
            } // End if domain dir can be read
          } // End if valid domain
          else {
            if (!in_array($domain, array('.', '..'))) {
              if ($conf['debug']) {
                echo "\tSkipping $domain\n";
              }
            }
          }
        } // End while loop for domains
        closedir($h_user);
      } // End if user dir can be read
    } // End if valid user
    else {
      if ($conf['debug']) {
        echo "Skipping $user\n";
      }
    }
  } // End while loop for users
  closedir($h_home);
} // End if home dir can be read

// Sort the mailboxes for alphabetic processing (for easy indication of progress)
ksort($mailboxes);

// Perform login checks
echo "\n\nChecking mailbox logins:\n";
$no_mailboxes = sizeof($mailboxes);
$no_passwords = ($conf['check_common_passwords'] ? sizeof($conf['common_passwords']) : 0) + 2;
echo "Checking $no_mailboxes mailboxes for $no_passwords passwords will take about " . ceil(($no_mailboxes * $no_passwords)/60) . " minutes\n";
foreach ($mailboxes as $index => $mailbox) {
  $logged_in = FALSE;
  $username = $mailbox['user'] . '@' . $mailbox['domain'];
  $local_passwords = array( $mailbox['user'], substr($mailbox['domain'], 0, strpos($mailbox['domain'], '.')) ); // User and the domain without extension
  if ($conf['check_common_passwords']) { // Optionally include common passwords in checks
    $passwords = array_merge($local_passwords, $conf['common_passwords']);
  }
  else {
    $passwords = $local_passwords;
  }
  echo "$username...";
  foreach ($passwords as $password) {
    if ($conf['debug']) {
      echo "\n\tchecking password $password";
    }
    $imap_login = imap_open('{localhost:143/imap/novalidate-cert/debug}INBOX', $username, $password, OP_HALFOPEN);
    if ($imap_login) {  // Weak password found
      imap_close($imap_login); // Close IMAP connection if it could be opened
      $logged_in = TRUE;
      $weak_mailboxes_found = TRUE;
      echo " logged in as user $username with password $password -- NOT GOOD!\n";
      fwrite($log, "$username with password $password\n");
      break;
    }
  }
  if (!$logged_in) {
    echo "good\n";
  }
}

// Close log file
if (!$weak_mailboxes_found) {
  fwrite($log, "No weak mailbox passwords found!\n");
}
fclose($log);

PS: Be patient while the script runs... It will take about one second per login attempt. In other words, allow at least the number of mailboxes multiplies by the number of passwords in seconds for it to complete its checks.

I hope you find this useful ;)