Thursday, October 29, 2009

Snort Email Alerts

I have a situation going on where I needed to watch for certain traffic matching a particular set of rules and of course the first thing that came to mind was to use snort. Snort allows me to write up very customizable rules so I knew I would have no problem detecting any of the problem traffic. In this particular case, I wanted to know if any SYN packets were being sent to a workstation. So the rule was pretty easy to write.
alert tcp any any -> $HOME_NET (msg:"Connection established to monitored machine"; flags:S; sid:20091029; rev:1;)
But I really didn't want to have to search through the logs looking for this kind of thing. After all, it might be months before I see the particular traffic that I'm looking for, so I decided that I wanted snort to email me if it say anything interesting. I don't, however, want for snort to email me about every alert that it sees because it tends to be a bit chatty at my site.

The way we get snort to treat alerts differently is to create a ruletype for the special rules. To do this, I went into my snort.conf file (usually located in /etc/snort/) and added the following lines:
ruletype emailalert
{
type alert
output alert_csv: csvlog default
}
Then I changed my rule above so that instead of being an alert, it was an emailalert:
emailalert tcp any any -> $HOME_NET (msg:"Connection established to monitored machine"; flags:S; sid:20091029; rev:1;)
So what we've done is told snort that if it sees any traffic matching my rule it should write something in csv form to a file called csvlog in whatever logging folder you're using. The reason I chose csv form is because everything gets written to a single line and I wont have to figure out down the road how to guess how many lines are in an alert message. Also, you can break the message down and only email the parts of the alert that you're interested in.

So what we could do is write a program that will read the file output and keep checking every few minutes to see if anything new has been added and then send an email alert. That would work, but you wouldn't really get real-time alerts. You would also have to put in code to find the end of the file and back off one line and keep track of what is new in the file. It all sounds messy. Instead, I used a named pipe. Snort is going to write to a file in its log directory called csvlog, so I change directory to /var/log/snort and used the following command to create a named pipe: mkfifo csvlog. Now all I have to do is write a program that will connect to the pipe and read a line from it. I don't have to deal with any of the other crap that I talked about above, and I can easily get real-time alerts. As usual, I went to my favorite programming language, python.

#! /usr/bin/env python

import smtplib
from email.mime.text import MIMEText

###################
# Here is the SMTP setup stuff
###################
smtpServer = "smtp.server.com"
smtpFrom = "nospam@blackfistsecurity.com"
smtpTo = "nospam@blackfistsecurity.com"

###################
# Here is a function for sending alert emails
###################
def SendAlertEmail(inMessage):
msg = 'Snort has detected an emailalert\n'
msg += inMessage
msg = MIMEText(msg)
msg['Subject'] = 'Emailalert from snort'
msg['From'] = smtpFrom
msg['To'] = smtpTo

s =smtplib.SMTP(smtpServer)
s.sendmail(smtpFrom,smtpTo,msg.as_string())
s.quit


###################
# Main program loop. Watch the named pipe and if
# anything shows up, email it using the function above
##################
infile = open('/var/log/snort/csvlog','r')
while True:
data = infile.readline()
if data:
SendAlertEmail(data)

The easy way to test this is to telnet to a port that you know is open on one of the monitored machines. If an email message pops up in your inbox, then you know you're golden. But what if you're looking at a machine that normally doesn't have any open ports? What if you're specifically looking for an instance where a rogue listener opens up? The rule I wrote will detect that, but how do you test it?

Easy. Just change the rule we wrote so that the flags we're looking for are just SYN packets rather than SYN-ACK packets. (flags:SA; becomes flags:S;). Send a kill -1 to the process that is running snort so that you're rule set will reload. Then telnet to any port on the machine and you should see an email message get sent. Do not nmap the machine (like I did) or you will suffer a flood of email messages that will make you want to cry! Don't forget to set the rule back to what it was and send another kill -1 to the snort process either.

6 comments:

Adam said...

Why did you import subprocess? I don't see where it is used.

Black Fist said...

@adam,
I think I might have started off thinking that I was going to use it and then forgot to take it out. There is no reason for it, and I have edited the post to remove it.

Todd said...

This works flawlessly for watching the named pipe .csv file that I have set up.

However, I cannot figure out why my "output alert_CSV" module is not working, nor can I find a good answer online as to how to even start fixing it. Any clues?

Black Fist said...

@Todd
I wonder if the command is case sensitive. I have been putting in alert_csv and you typed alert_CSV. Also, I would check and make sure that you've got everything in brakets. If you want to send me your snort.conf file I can take a look and see if the answer jumps out at me.

ten-seven said...

This looks adaptable to outputting selected alerts via Twitter. I know there's at least one other solution out there, but I have TTYtter already doing something else, and it's OAuth-capable now.

Rich Johnson said...

Great article! You mentioned "Then I changed my rule above so that instead of being an alert, it was an emailalert:"

Where do you make this change? Just remove the lines you created in snort.conf and only put in the new lines? If that is the case, why not just remove what is replace so it's not confusing?