Well well.. today I had to sort out a spam run that was originating from our webmail and boy was I happy to have some way to link email to some account.
But .... eeeek I saw something I didn't want to be real
Let's talk about the way SquirrelMail "encrypts" the headers it adds to mail for tracking purposes.
Purpose of those headers
The two headers that SquirrelMail adds are one for the user name from which the mail was sent (e.g. authenticated user name) and the other is some kind of hash on the from field for which I don't quite care for the purposes of killing spammers.
Format of those headers
When you look at an email that was sent by SquirrelMail (and for which you set the variable $encode_header_key -- which should be done automatically on debian -- you can see something that will look like the following:
X-Squirrel-UserHash: KVcBIR8rLwwFNDxaLkpWew==
X-Squirrel-FromHash: fQAIWRtqdgJ/
How are they generated?
How the heck is that scheiße generated?
OK let's dig into the code that generates the headers. In file /usr/share/squirrelmail/class/deliver/Deliver.class.php, inside function prepareRFC822_Header() we can find the following two lines of interest (with 3rd line before just to give more context):
$rn = "\r\n";
[...]
$header[] = 'X-Squirrel-UserHash: '.OneTimePadEncrypt($username,base64_encode($encode_header_key)).$rn;
$header[] = 'X-Squirrel-FromHash: '.OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key)).$rn;
... wait what did we just see?
... yep, the code from file /usr/share/squirrelmail/functions/strings.php does confirm it. It is a one time pad.
So the headers are encrypted with a one time pad with a never-changing-key. Is it just me or is that the textbook example of how to NOT use a one time pad?
I will leave reproducing this as an exercise to the reader, but I will claim that if you're able to catch a few dozen email with such headers, you will be able to easily retrieve part of the key -- enough to be able to decrypt most headers that you intercept.
How do I decrypt those headers?
Simple, I've written a small script for that that actually uses the function from SquirrelMail's code. Shame we can't just include the file that defines the functions though, so I just copied the function definition in the script. It's tailored for Debian since the $encode_header_key variable is defined in /etc/squirrelmail/config_local.php but you can easily change this to get the key from wherever it's lying on your disk:
#!/usr/bin/php
<?php
/**
* Decrypts a password from the cookie
*
* Decrypts a password from the cookie, encrypted by OneTimePadEncrypt.
* This uses the encryption key that is stored in the session.
*
* @param string string the string to decrypt
* @param string epad the encryption key from the session
* @return string the decrypted password
*/
function OneTimePadDecrypt ($string, $epad) {
$pad = base64_decode($epad);
if (strlen($pad)>0) {
// make sure that pad is longer than string
while (strlen($string)>strlen($pad)) {
$pad.=$pad;
}
} else {
// FIXME: what should we do when $epad is not base64 encoded or empty.
}
$encrypted = base64_decode ($string);
$decrypted = '';
for ($i = 0; $i < strlen ($encrypted); $i++) {
$decrypted .= chr (ord($encrypted[$i]) ^ ord($pad[$i]));
}
return $decrypted;
}
set_include_path('/etc');
include "/etc/squirrelmail/config_local.php";
while (! feof(STDIN)) {
$val = fgets(STDIN);
$res = OneTimePadDecrypt($val, base64_encode($encode_header_key));
print ($res);
}
To use the script, just invoke it as a script within your shell of choice and for each line of text that you give to it on stdin, it will return the decrypted value to stdout until it hits EOF.
So ... what's all this fuss about?
If leaking your usernames is bad for you, DON'T USE THE SQUIRRELMAIL ENCRYPTED HEADERS. To disable them, you can just set the $encode_header_key variable to an empty string.
The downside to not using the headers is that you can't easily track down trouble makers. But you can always enable the headers when you need to track such an annoyance.