====== SnortWarn ====== This python script uses snort's database event logs and the whois database to send scheduled e-mails to abuse ISP-s about incidents. #!/usr/bin/python import sys import os import re import pprint from pyPgSQL import PgSQL from sets import Set import smtplib from email.MIMEText import MIMEText from email.MIMEMultipart import MIMEMultipart _DB=None _DBname="snortdb" _DBuser="snortdb" _DBpass="password" _DBhost="localhost" primaryrecipient="email@add.ress.hu" treshold=5 # minimum no. of events to mail mailhead=""" Hello! This is an automatically generated message from the netpolice robot at MTA SZTAKI Network Security Department. You can reach us at netpolice@sztaki.hu so please DO NOT REPLY to this mail's sending address as your reply will be silently discarded. We are to inform you about several security violations originating from your address space. Please note that our detection system uses an IP packet pattern database so false positives can never be completely eliminated. If you are sure that our warning was pointless please discard this letter, we are sorry for your inconvenience. Your e-mail address has been carefully harvested from the attached whois record. If this message is not for you to concern please tell the responsible person or group to set up an e-mail address beginning with the word 'abuse' and ask your whois administrator to update the record. Our system cannot distinguish between different abuse adresses established for different types of incidents. ---------------------------------------------------------------------- The following activities were detected (you can find more information as the destination port address and other details on the URLs below): %s The activities above were detected from the following source addresses: %s The detailed transcript of the events follows. Note that our system logs the same action from the same source or destination address once per 60 seconds so the actual event rate could be higher. My time zone is CE(S)T (CET with or without daylight saving). See: http://www.timeanddate.com/worldclock/city.html?n=50 ---------------------------------------------------------------------- """ print "snortwarn.py starting ...\n" pp = pprint.PrettyPrinter(indent=4) def int2dquad(int32): a=str((int32 & 0x0ff000000) >> 24) b=str((int32 & 0x000ff0000) >> 16) c=str((int32 & 0x00000ff00) >> 8) d=str((int32 & 0x0000000ff)) return a+"."+b+"."+c+"."+d def db_connect(): global _DB try: _DB = PgSQL.connect(database=_DBname,user=_DBuser,password=_DBpass,host=_DBhost) except PgSQL.Error, msg: print "Connection to database '%s' failed" % dbname print msg, sys.exit() return _DB def db_query(query): global _DB if _DB is None: _DB=db_connect() cur = _DB.cursor() try: cur.execute(query) except PgSQL.Error, msg: print "db_query failed\n%s" % msg, sys.exit() desc={}; i=0 for r in cur.description: desc[i]=r[0] i=i+1 try: cur.arraysize=1000; res = cur.fetchall() except StandardError, msg: print "db_query: fetch of all instanaces failed\n%s" % msg, sys.exit() result={} i=0 for r in res: u=0 result[i]={} for d in desc.values(): result[i][d] = r[u] u=u+1 i=i+1 cur.close() _DB.commit() return result print "fetching alerts from database ..." res=db_query(""" select event.\"timestamp\",signature.sig_name,signature.sig_sid,iphdr.ip_src,iphdr.ip_dst from event,signature,iphdr where iphdr.ip_ttl>0 and iphdr.cid=event.cid and signature.sig_id=event.signature and event.\"timestamp\"> timestamp 'now' - interval '24 hours' and signature in ( select s.sig_id from signature s,sig_class c where s.sig_class_id=c.sig_class_id and c.sig_class_name in ( 'web-application-attack','attempted-admin','misc-attack','denial-of-service','attempted-dos' ) ) """) # order by timestamp print "got " + str(len(res)) + " events ..." # group by ip ips={} for r in res.values(): src=int2dquad(r['ip_src']) if not ips.has_key(src): ips[src]={'count':0, 'alerts': Set(), 'events':{}} ips[src]['count']+=1 ips[src]['events'][r['timestamp']]=r ips[src]['alerts']|=Set([str(r['sig_sid'])+"|"+r['sig_name']]) #pp.pprint(ips) # fetch whois for each ip for r in ips: if ips[r]['count']<3: # drop low noise ips print "dropping "+r+" (bellow alarm treshold)..." ips[r]=None continue print "collecting whois data for "+r+" ..." whois='' whoisp=os.popen("jwhois "+r) whois+=whoisp.read(1024000); whoisp.close(); # whoisp=os.popen("jwhois +"+r) # whois+=whoisp.read(1024000); # whoisp.close(); e=None mailregex="[0-9a-z_.-]*@[0-9a-z_.-]+[.][a-z]+" s=Set() s|=Set(re.compile('(?ims)([0-9a-z_.-]*abuse'+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)abuse.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)incident.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)spam.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)role.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)person.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)route.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)notify.*?('+mailregex+')').findall(whois)) if len(s) == 0: s|=Set(re.compile('(?ims)('+mailregex+')').findall(whois)) if len(s) > 0: e=list(s) # loopback or private ips: no notify if len(re.compile('(?ims)(127[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}|172[.]16[.][0-9]{1,3}[.][0-9]{1,3}|192[.]168[.][0-9]{1,3}[.][0-9]{1,3})').findall(r))>0: print " loopback or private ip " + str(r); # e=[primaryrecipient] e=["mcree@sztaki.hu"] # do not notify ripe or arin technical addresses and our own address if len(re.compile('(?ims)(@ripe.net|@arin.net|@arpnic.net|@nic[.])').findall(str(e)))>0: print " dropping whois technical mail address " + str(e); # e=[primaryrecipient] e=["mcree@sztaki.hu"] # local address if len(re.compile('(?ims)(@sztaki.hu)').findall(str(e)))>0: print " dropping whois technical mail address " + str(e); # e=[primaryrecipient] e=["mcree@sztaki.hu", "btoth@sztaki.hu"] ips[r]['whois']=whois if e is not None: ips[r]['email']=e else: ips[r]['email']=None print " WARNING: no mail address found!" #pp.pprint(ips) # group by emails emails={} for r in ips: if ips[r] is not None: e=ips[r]['email'] if e is not None: if not emails.has_key(str(e)): emails[str(e)]={'count':0, 'alerts':Set(), 'whois':ips[r]['whois'], 'ips':[], 'email':e} emails[str(e)]['ips']+=[r] emails[str(e)]['count']+=ips[r]['count'] emails[str(e)]['alerts']|=ips[r]['alerts'] #pp.pprint(emails) #pp.pprint(ips) for e in emails: if emails[e]['count']>=treshold: print "sending mail to "+e attsum="" for a in emails[e]['alerts']: (sid,desc)=a.split('|') attsum+=desc+"\n" attsum+=" -> http://www.snort.org/snort-db/sid.html?sid="+sid+"\n" ipseach="" ipsuml=[] for i in emails[e]['ips']: ipseach+=i+" ("+str(ips[i]['count'])+" event(s))\n" for a in ips[i]['alerts']: (sid,desc)=a.split('|') ipseach+=" "+desc+"\n" for v in ips[i]['events'].values(): ipsuml+=[str(v['timestamp'])+' '+int2dquad(v['ip_src'])+" -> "+int2dquad(v['ip_dst'])+' '+v['sig_name']+"\n"] ipseach+="" ipsuml.sort() if(len(ipsuml)>100): ipsuml=ipsuml[0:99] ipsum="" ipsum=ipsum.join(ipsuml) ipsum="Transcript follows (limited to 100 records):\n\n"+ipsum+"\n----------------------------------------------------------------------\n\n" #print mailhead % (attsum, ipseach) #print ipsum commaspace=', ' msg=MIMEMultipart() msg['Subject']="Security Notification" msg['From']="devnull@sztaki.hu" msg['To']=commaspace.join([primaryrecipient] + emails[e]['email']) msg.attach(MIMEText((mailhead % (attsum, ipseach)) + ipsum)) # msg.attach(MIMEText(ipsum)) msg.attach(MIMEText("Whois query result follows:\n\n"+emails[e]['whois'])) msg.preamble = 'You should view this message MIME-aware mail reader.\n' msg.epilogue = '' s = smtplib.SMTP() s.connect() for recip in ([primaryrecipient] + emails[e]['email']): # print "sending mail to " + str(recip) s.sendmail("devnull@sztaki.hu", recip, msg.as_string()) s.close() #print msg.as_string() {{tag>util security email notify snort alert python}} ~~LINKBACK~~