Redmine uses sha1 to store passwords, salted with a random uhm.. salt.
In pseudo code the algorithm to create a password hash would look like this:
sha1(salt + sha1(password))
I made a dump of the users table in the format "login:password_hash:salt" and wrote a small Python based cracker that could parse this and iterate through a dictionary file.
Out of the 16 users in the table 2 of them used their login as their password.. 1 used his first name and 1 used a password that was cracked within seconds.
Needless to say, I locked their accounts [insert evil laugh] and I'm going to do this more often. (Maybe automated, with email notifications or something.)
O, and for those who want to do this sort of unethical thing too, here's the script:
#!/usr/bin/env python from optparse import OptionParser import hashlib parser = OptionParser(usage="usage: %prog [options] userfile wordlist") (options, args) = parser.parse_args() if len(args) != 2: parser.error("Incorrect number of arguments") def import_users(): user_file = open(args[0], 'r') users = [] # format: (login, password_hash, salt) for line in user_file.readlines(): if not line.startswith('#'): users.append(line.strip().split(':')) user_file.close() return users def calculate_hash(password, salt): hashed_password = hashlib.sha1(password).hexdigest() hash = hashlib.sha1(salt+hashed_password).hexdigest() return hash users = import_users() # Try username as password cracked = [] for user in users: word = user[0].strip() if user[1] == calculate_hash(word, user[2]): print "Password of user '%s' is '%s'" % (user[0], word) cracked.append(user) for user in cracked: users.remove(user) wordlist = open(args[1], 'r') for word in wordlist: cracked = [] for user in users: if user[1] == calculate_hash(word, user[2]): print "Password of user '%s' is '%s'" % (user[0], word) cracked.append(user) for user in cracked: users.remove(user) wordlist.close()