001: #!/usr/bin/python
002: 
003: # pyauthanalyzer.py by Matti Aronen 2005. Released under GPL.
004: # version 0.12
005: 
006: # Run this script daily from cron. No need to run more often.
007: 
008: # NOTICE that this script currently supports only IPv4.
009: 
010: # Example log format:
011: #
012: # Date  | Time   | host   | sshd ID     | grep word  | user|    |  IP           |
013: # Apr  1 03:13:37 computer sshd[604753]: Illegal user haxor from 123.123.123.123
014: #
015: # NOTE: Last item in the log entry MUST be the IP address for this to work. It's all that matters.
016: # If your log format differs, it's quite easy to modify this script to work.
017: 
018: # Required available shell commands: grep, whois, wc
019: 
020: #------- Configuration -------
021: 
022: # How many tries from a single IP to take action? DON'T USE LESS THAN 15. We are not spammers here
023: tries_to_action = 15
024: 
025: # Where is the authentication log file? This must be readable for the user running this script
026: log_file = "/var/log/auth.log"
027: 
028: # Grep with what to find attack lines (blank spaces must be escaped with "\")
029: grep_word = "Illegal\ user"
030: 
031: # How many days to keep IPs in reported register? 
032: # _DO_NOT_ USE LESS THAN THE TIME YOUR AUTH.lOG CYCLES! We don't want to re-report anything (spam ISP)
033: # 32 is good for just being sure, more is always better
034: reported_days = 32
035: 
036: # Path to tmp
037: tmp_path = "/tmp"
038: 
039: # Path to /var/tmp if not /var/tmp
040: var_tmp_path = "/var/tmp"
041: 
042: #--- Mail settings ---
043: 
044: # Which smtp server to use?
045: smtp_server = "localhost"
046: 
047: # Does it require authentication? (0 = no, 1 = yes)
048: smtp_auth = 0
049: # If does:
050: smtp_username = "user"
051: smtp_password = "password"
052: 
053: 
054: # What is your e-mail address? (Will be used as From:)
055: my_email = "my.name@my.domain"
056: 
057: # Send me a copy of abuse messages? (0 = no, 1 = yes)
058: mail_me = 1
059: 
060: # To make sure you have modified the config, change to 1
061: configured = 0
062: 
063: #------- Don't touch anything under this line -------
064: import string, os, re, smtplib, time, sys
065: 
066: pplog_file = tmp_path + "/pyauthanalyzer.parsed.log.tmp"
067: iplog_file = var_tmp_path + "/pyauthanalyzer.reported.ip.log"
068: iplist = []
069: ips_to_report = []
070: reported_udays = reported_days * 86400  # 86400 = one day in unix time
071: # Which e-mail addresses to look for
072: admin_mails = ['abuse', 'hostmaster', 'admin',
073:                 'root', 'operator', 'administrator', 
074:                 'support', 'techsupport', 'techies', 'webmaster']
075: 
076: def get_ips():
077:         # Create a list of unique IP addresses from the attack log
078:         f = open(pplog_file, 'r')
079:         for i in f:
080:                 si = re.sub("::ffff:", '', str.split(i)[-1]) # Quick'n'dirty for IPv4 mapped addresses (IPv6)
081:                 if si not in iplist:
082:                         iplist.append(si)
083:         f.close()
084:         
085: def check_reported(ip):
086:         # Search for the IP in already reported IPs list
087:         if os.path.isfile(iplog_file):
088:                 f = open(iplog_file, 'r')
089:                 data = f.readlines()
090:                 f.close()
091:                 for i in data:
092:                         if str.split(i)[0] == ip:
093:                                 # print 'Not reporting %s' % ip # debug
094:                                 return 'True'
095:         return
096: def clean_files():
097:         # Remove tmp file and remove outdated reported IP addresses from the list
098:         if os.path.isfile(pplog_file):
099:                 os.remove(pplog_file)
100:         if os.path.isfile(iplog_file):
101:                 f = open(iplog_file,'r')
102:                 data = f.readlines()
103:                 f.close()
104:                 f = open(iplog_file,'w') # Rewrite the file from scratch
105:                 for i in data:
106:                         if not float(time.time()) - float(str.split(i)[1]) > reported_udays:
107:                                 f.write(i)
108:                 f.close()
109:                 
110: 
111: def check_ip(ip):
112:         # Check if we should we report this ip
113:         rgrep = os.popen('grep %s %s | wc -l' % (ip, pplog_file), 'r', 1)
114:         times_tried = int(rgrep.read())
115:         rgrep.close()
116:         return times_tried
117: 
118: def parse_log():
119:         # For speed, we use the unix grep command to parse out the lines we are interested in from larger logs
120:         os.system('grep %s %s > %s' % (grep_word, log_file, pplog_file))
121: 
122: def get_abuse_address(ip):
123:         # Try to determine the abuse e-mail address. Cludgy piece of shit, but works
124:         mails = []
125:         whois_c = os.popen('whois %s' % ip, 'r', 1)
126:         whois_data = map(str.strip, whois_c.readlines())
127:         whois_c.close()
128:         for i in str.split(str(whois_data)):
129:                 if re.search("(.+@.+)", i):
130:                         pure_addr = re.sub('mailto:', '', str.strip(i, "',"))
131:                         if pure_addr not in mails: # We SHOULD have only the e-mail address by now
132:                                 mails.append(pure_addr)
133:         if len(mails) == 0:
134:                 return None
135:         for i in admin_mails:
136:                 for j in mails:  # Check if any of the addresses start with admin_mails
137:                         if i+"@" in j:
138:                                 return j
139:         return None # If not, return None. No mail will be sent.
140:         
141: def send_abusemail(abuse_mail, ip, attack_log):
142:         # Send an abuse report to the attackers ISP
143:         # print "Sending mail to %s for %s" % (abuse_mail, ip) #debug
144:         abuse_msg = '''Subject: Abuse from %s\n
145: Greetings.\n
146: This automated message has been sent to you in order to report automated \
147: attack(s) from IP %s that belongs to your network. Chances are that the \
148: computer has been hacked and the owner doesn\'t know anything about this.\n
149: Here is a short sample of the attack log:
150: %s
151: This message is generated by pyauthanalyzer.
152: ''' % (ip, ip, attack_log)
153:         recipients=[]
154:         recipients.append(abuse_mail)
155:         if mail_me == 1:
156:                 recipients.append(my_email)
157:         session = smtplib.SMTP(smtp_server)
158:         if smtp_auth == 1:
159:                 session.login(smtp_username, smtp_password)
160:         session.sendmail(my_email, recipients, abuse_msg)
161:         session.quit()
162:         #print abuse_msg, recipients # debug
163: 
164: def report_ip(ip):
165:         # Get abuse e-mail address and report the IP
166:         abuse_mail = get_abuse_address(ip)
167:         if abuse_mail is not None: # If we have an address, send message
168:                 f = os.popen('grep \'%s\' %s | head -n 20' % (ip, pplog_file), 'r', 1)
169:                 data = f.readlines()
170:                 f.close()
171:                 attack_log = "".join(data)
172:                 send_abusemail(abuse_mail, ip, attack_log) # finally send the mail
173:         f = open(iplog_file,'a')
174:         f.write('%s %s\n' % (ip, time.time())) # Write down that this IP has been reported
175:         f.close()
176: 
177: def main():
178:         if configured == 0:
179:                 print "You must edit the configuration first"
180:                 sys.exit(1)
181:         parse_log()
182:         get_ips()
183:         for ip in iplist:
184:                 if not check_reported(ip):
185:                         if check_ip(ip) >= tries_to_action:
186:                                 ips_to_report.append(ip)
187:         for ip in ips_to_report:
188:                 report_ip(ip)
189:         clean_files()
190: 
191: if __name__ == '__main__':
192:         main()