Wednesday, February 6, 2008

Passwords in SMF 1.1.4

I'm posting this in case someone else are struggling with SMF password encrypting like I did.

SMF 1.1.4 uses SHA-1 with a salt. You would think that the passwordSalt in the database is used as the salt, but it isn't. That's probably a field that was used in old versions. Instead, the membername is used as the salt, but *before* the password, not *after*, as most search results indicated.

I finally found this link to a php file that is *not* in the distribution.
function smf_registerMember(
[...]
$register_vars = array(
'memberName' => "'$username'",
'realName' => "'$username'",
'passwd' => '\'' . sha1(strtolower($username) . $password) . '\'',
'passwordSalt' => '\'' . substr(md5(rand()), 0, 4) . '\'',


So I tried this Java code:
sha1(username.toLowerCase() + password);
That worked!

Here is the source for the sha1 method:
   public static String sha1(String data)
{
byte[] bytes = data.getBytes();
try
{
MessageDigest md5er = MessageDigest.getInstance("SHA-1");
byte[] hash = md5er.digest(bytes);
return bytes2hex(hash);
}
catch (GeneralSecurityException e)
{
throw new RuntimeException(e);
}
}

private static String bytes2hex(byte[] bytes)
{
StringBuffer r = new StringBuffer(32);
for (int i = 0; i < bytes.length; i++)
{
String x = Integer.toHexString(bytes[i] & 0xff);
if (x.length() < 2)
r.append("0");
r.append(x);
}
return r.toString();
}

6 comments:

Anonymous said...

Nice Work!

For anyone else searching for how to do this in PHP, it ends up being:

function old_encrypt($password, $user) { // password encryption algorythm for old SMF members
$key = str_pad(strlen($user) <= 64 ? $user : pack('H*', md5($user)), 64, chr(0x00));
return md5(($key ^ str_repeat(chr(0x5c), 64)) . pack('H*', md5(($key ^ str_repeat(chr(0x36), 64)). $password)));
}

function new_encrypt($password, $user) {// password encryption method in the new SMF 1.1.4
return sha1(strtolower($user).$password);
}

(Use old_encrypt to check the passwords of users who are still in the old format, and new_encrypt to check users who have logged into SMF since the upgrade to 1.1.4)

Anonymous said...

Oh my god! You saved my live guys. Thank you very much. Really good work :)

Michael Hasenstein said...

I have not tried it, but according to smf_api.php (version 1.1 from 2006) the function is

$password = sha1(sha1(strtolower($username) . $password) . $salt);


where $salt is

$result = smf_query("SELECT memberName, passwordSalt FROM $smf_settings[db_prefix]members
WHERE ID_MEMBER = '" . (int) $id . "'
LIMIT 1", __FILE__, __LINE__);
list ($username, $salt) = mysql_fetch_row($result);

kub9001 said...

Michael, you are right, but it works only "if (!$encrypted)" and the default value on $encrypted is true

Anonymous said...

Hello,

I have direct access to a SMF forum 1.1.13 and to the database. I need to get 2 passwords decrypted. Is this possible, how can I do it. Please help?

Please email me at games .)at(. godvs.net

Lars said...

This code only helps with encryption and comparison of passwords, not with decryption.