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()