Perl SMTP AUTH

•Introduction:
For months I have been trying to create a Perl SMTP AUTH script that would allow me to log into my mail server, that requires authentication, from a perl script and send mail. I was under the impression that the encryption was CRAM-MD5, however I found that it other encryptions are acceptable and are much easier than this. I have tried this script on a number of servers and it appears to work well. I must thank Justin Clark for sparking my interest in this again.

 

•Script:
The script that you can see to the right is just a simple sockets version of SMTP communication. It does not send any mail so as not to flood my server with mail. I have also diabled the account it goes to for fear of some not so trustworthy people. The script takes the server information, username and password then uses these to log in to the server after it has encrypted the username and password. SMTP AUTH was originally created try and prevent spam. Almost all major mail clients support it, and quite a few servers implement it. The issue arises when you try and create a perl script to communicate with these servers. I was unable to find anything about this topic, and Net::SMTP, the standard perl SMTP module, does not support SMTP_AUTH.

 

•Notes
Please note the following font definitions:
Incoming line from server
Outgoing line to server
Special

 

•Authentication Types
When a client first logs on to a server it receives a similar response to below:

250-pioneer.host-serve.net
250-AUTH=LOGIN CRAM-MD5
250-AUTH LOGIN CRAM-MD5
250-PIPELINING
250 8BITMIME


The lines that we are interested in are the AUTH lines. Most servers do the AUTH and AUTH= line I assume that there was some disagreement in the RFC as to how this was supposed to be parsed so both lines are included. Other servers also put the AUTH commands on seperate lines so just be prepared to see anything. My server is telling me that it will accept LOGIN and CRAM-MD5 authentication methods. There are three main methods with the last being PLAIN which is what old servers ask for.

 

•Inform the sever we wish to use AUTH
To let the server know that we want to use authentication we send the command:

AUTH ***

Where the *** is filled in with one of the servers available AUTH methods such as LOGIN.

 

•PLAIN
First let us look at the PLAIN log in type. After we send the AUTH PLAIN command we receive the line:

334 Username:

We then send the username in plain text and we receive the next line:

334 Password:

We then send the password in plain text and if everything is correct we receive the line:

235 Go Ahead

If we sent the wrong user/pass combination we receive:

535 auth failure

As you can see the PLAIN method offers no protection from packet sniffers seeing your password.

 

•LOGIN
This offers only slightly more protection from packet sniffing. To start we send the server the following line:

AUTH LOGIN

And we receive the line:

334 VXNlcm5hbWU6

Which Username: encoded in base64. We then send the server our username encoded in base64 and we receive the line:

334 UGFzc3dvcmQ6

Whic is Password: encoded in base64. We then send the server our password encoded in base64 and we receive the following line:

235 Go Ahead

If we sent a bad combination we receive the line:

535 auth failure

As you can see LOGIN only provides a mask of your username and password. However an intelligent person can still view the packets for this and unencode your username and password, or even just reuse it since the encoding never changes.

 

•CRAM-MD5
This method provides real undecryptable authetication that changes with every login. To start this we tell the server:

AUTH CRAM-MD5

We then receive a similar line to the following:

334 PDIzODkxLjEwMTU5ODM0OTNAcGlvbmVlci5ob3N0LXNlcnZlLm5ldApaAj4=

If we decode base64 this line it should say:

<23891.1015983493@pioneer.host-serve.net>

(However my server has a glitch in its CRAM-MD5 authentication and it sends extra characters that mess up the authentication). We then respond with the following line:

ZGVtbyBlMDRjMDkyMDA5NzY3MWE3NmM3YzAyMzhkMjU2ZTdjMg==

If we base64 decode this line it says:

demo e04c0920097671a76c7c0238d256e7c2

We can see that it says demo which is my username and some junk after it. But what is this junk? The junk is the previous server ticket, that funny line that looks like an email address of numbers. Hashed and CRAM-MD5 encoded with my password. The server ticket changes every millisecond so my response never looks the same and the CRAM-MD5 cannot be undone. So how does the server verify me? It does the same process on its end and looks at the two responses, if they match you can enter. Basically your password changes every millisecond and only the server and you know how to create the new password each millisecond. If authentication works we get the following message:

235 Go Ahead

If we sent a bad combination we receive the line:

535 auth failure

 

•How to build a CRAM-MD5 Response in perl
This is actually very easy you will need the modules:

MIME::Base64;
Digest::HMAC_MD5

I will assume that you can figure out how to use MIME::BASE64 from my script example. To call HMAC_MD5 you need to specify the following use:

use Digest::HMAC_MD5 qw(hmac_md5 hmac_md5_hex);

Then in your code you need to base64 unencode the session ticket from the server. Then pass both the unencoded session ticket and your password into the following command:

$encPass = hmac_md5_hex($ticket, $password);

Next you need to add your username to the front of the $encPass string. Finally Base64 encode this and send it to the server.

I am sorry I have not added CRAM-MD5 to my sample script but I am having some difficulties with my server, however this will work with other servers I have been able to successfully try it on 5 servers.

Perl SMTP AUTH

See the script in action, click here.

 

Quick Links