├── README.md ├── ParseLogs.py └── logalyzer.py /README.md: -------------------------------------------------------------------------------- 1 | logalyzer 2 | ========= 3 | 4 | Logalyzer is a script I wrote to make looking through the auth logs neater. It can parse users, IP addresses, failures, etc. 5 | 6 | Some examples: 7 | 8 |
9 | # python logalyzer.py -h 10 | Usage: logalyzer.py [options] 11 | 12 | Options: 13 | -h, --help show this help message and exit 14 | -u Specify user. Blank lists all users. 15 | --full Full log dump for specified user 16 | -l LOG Specify log file. Default is auth.log 17 | -f List failures 18 | -s List success logs 19 | -c List commands by user 20 | -i List IP Addresses 21 | 22 | Combine flags to view user-specific information. '-u test -i' lists IP 23 | addresses associated with user test 24 | 25 | # sudo python logalyzer.py -u bryan 26 | [!] Log file: /var/log/auth.log 27 | [!] Logs associated with user 'bryan' 28 | [+] First log: Jul 24 21:26:09 29 | [+] Last log: Jul 24 23:38:26 30 | [!] Failure Logs 31 | [!] Success Logs 32 | [!] Associated IPs 33 | 192.168.1.118 34 | [!] Commands 35 | /usr/bin/apt-get dist-upgrade 36 | /usr/local/bin/python logalyzer.py -l /var/log/auth.log.1 -u bryan 37 | /usr/local/bin/python logalyzer.py -u bryan -l /var/log/auth.log.1 -s 38 | 39 | # python logalyzer.py -l /var/log/auth.log.1 -u bryan -c 40 | [!] Log file: /var/log/auth.log.1 41 | [+] Commands for user 'bryan' 42 | /usr/bin/apt-get update 43 | /usr/bin/apt-get dist-upgrade 44 |45 | -------------------------------------------------------------------------------- /ParseLogs.py: -------------------------------------------------------------------------------- 1 | import re 2 | import gzip 3 | 4 | # 5 | # ParseLogs.py 6 | # Parsing component of Logalyzer. Compiled in Python 2.6 7 | # 8 | 9 | # log object 10 | # Stuck into a dictionary by user:Log, where log houses 11 | # logs, fails, successes, logged IPs, and commands used 12 | class Log: 13 | # dump date of first log 14 | def first_date(self): 15 | if len(self.logs) > 0: 16 | date = None 17 | i = 0 18 | # sometimes the first few aren't right, so look 19 | # until we find one 20 | while i < len(self.logs) and date is None: 21 | date = ParseDate(self.logs[i]) 22 | i += 1 23 | return date 24 | # dump date of last log 25 | def last_date(self): 26 | if len(self.logs) > 0: 27 | return ParseDate(self.logs[len(self.logs) - 1]) 28 | def __init__(self, usr): 29 | self.usr = usr 30 | self.logs = [] 31 | self.fail_logs = [] 32 | self.succ_logs = [] 33 | self.ips = [] 34 | self.commands = [] 35 | 36 | # parse user from various lines 37 | def ParseUsr(line): 38 | usr = None 39 | if "Accepted password" in line: 40 | usr = re.search(r'(\bfor\s)(\w+)', line) 41 | elif "sudo:" in line: 42 | usr = re.search(r'(sudo:\s+)(\w+)', line) 43 | elif "authentication failure" in line: 44 | usr = re.search(r'USER=\w+', line) 45 | elif "for invalid user" in line: 46 | usr = re.search(r'(\buser\s)(\w+)', line) 47 | if usr is not None: 48 | return usr.group(2) 49 | 50 | # parse an IP from a line 51 | def ParseIP(line): 52 | ip = re.search(r'(\bfrom\s)(\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)', line) 53 | if ip is not None: 54 | return ip.group(2) 55 | 56 | # parse a date from the line 57 | def ParseDate(line): 58 | date = re.search(r'^[A-Za-z]{3}\s*[0-9]{1,2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}', line) 59 | if date is not None: 60 | return date.group(0) 61 | 62 | # parse a command from a line 63 | def ParseCmd(line): 64 | # parse command to end of line 65 | cmd = re.search(r'(\bCOMMAND=)(.+?$)', line) 66 | if cmd is not None: 67 | return cmd.group(2) 68 | 69 | # begin parsing the passed LOG 70 | def ParseLogs(LOG): 71 | # initialize the dictionary 72 | logs = {} 73 | 74 | # parse the log 75 | f = None 76 | try: 77 | f = gzip.open(LOG, 'r') if '.gz' in LOG else open(LOG, 'r') 78 | log = f.read() 79 | except Exception, e: 80 | print '[-] Error opening \'%s\': %s'%(LOG,e) 81 | return None 82 | finally: 83 | if f is not None: f.close() 84 | 85 | for line in log.split('\n'): 86 | # match a login 87 | if "Accepted password for" in line: 88 | usr = ParseUsr(line) 89 | 90 | # add 'em if they don't exist 91 | if not usr in logs: 92 | logs[usr] = Log(usr) 93 | 94 | ip = ParseIP(line) 95 | # set info 96 | if not ip in logs[usr].ips: 97 | logs[usr].ips.append(ip) 98 | logs[usr].succ_logs.append(line.rstrip('\n')) 99 | logs[usr].logs.append(line.rstrip('\n')) 100 | 101 | # match a failed login 102 | elif "Failed password for" in line: 103 | # parse user 104 | usr = ParseUsr(line) 105 | 106 | if not usr in logs: 107 | logs[usr] = Log(usr) 108 | 109 | ip = ParseIP(line) 110 | 111 | if not ip in logs[usr].ips: 112 | logs[usr].ips.append(ip) 113 | logs[usr].fail_logs.append(line.rstrip('\n')) 114 | logs[usr].logs.append(line.rstrip('\n')) 115 | 116 | # match failed auth 117 | elif ":auth): authentication failure;" in line: 118 | # so there are three flavors of authfail we care about; 119 | # su, sudo, and ssh. Lets parse each. 120 | usr = re.search(r'(\blogname=)(\w+)', line) 121 | if usr is not None: 122 | usr = usr.group(2) 123 | # parse a fail log to ssh 124 | if "(sshd:auth)" in line: 125 | # ssh doesn't have a logname hurr 126 | usr = ParseUsr(line) 127 | if not usr in logs: 128 | logs[usr] = Log(usr) 129 | logs[usr].ips.append(ParseIP(line)) 130 | # parse sudo/su fails 131 | else: 132 | if not usr in logs: 133 | logs[usr] = Log(usr) 134 | logs[usr].fail_logs.append(line.rstrip('\n')) 135 | logs[usr].logs.append(line.rstrip('\n')) 136 | # match commands 137 | elif "sudo:" in line: 138 | # parse user 139 | usr = ParseUsr(line) 140 | if not usr in logs: 141 | logs[usr] = Log(usr) 142 | 143 | cmd = ParseCmd(line) 144 | # append the command if it isn't there already 145 | if cmd is not None: 146 | if not cmd in logs[usr].commands: 147 | logs[usr].commands.append(cmd) 148 | logs[usr].logs.append(line.rstrip('\n')) 149 | return logs 150 | -------------------------------------------------------------------------------- /logalyzer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys, stat, os 4 | from optparse import OptionParser 5 | 6 | import ParseLogs 7 | 8 | # 9 | # Logalyzer. Compiled with python 2.6 10 | # 11 | 12 | # callback for the user flag 13 | def user_call(option, opt_str, value, parser): 14 | if len(parser.rargs) is not 0: 15 | value = parser.rargs[0] 16 | else: 17 | value = None 18 | setattr(parser.values, option.dest, value) 19 | 20 | # entry 21 | if __name__=="__main__": 22 | 23 | # default location 24 | log = '/var/log/auth.log' 25 | 26 | # parsing options 27 | parser = OptionParser(epilog= 28 | "Combine flags to view user-specific information. \'-u test -i\' lists IP addresses " 29 | "associated with user test") 30 | parser.add_option("-u", help="Specify user. Blank lists all users.", action="callback", 31 | callback=user_call, default=None, dest="user") 32 | parser.add_option("--full", help="Full log dump for specified user", action="store_true", 33 | default=False, dest="fullu") 34 | parser.add_option("-l", help="Specify log file. Default is auth.log", default=None, dest="log") 35 | parser.add_option("-f", help="List failures", action="store_true", default=False, dest="fail") 36 | parser.add_option("-s", help="List success logs", action="store_true", default=False, dest="success") 37 | parser.add_option("-c", help="List commands by user", action="store_true", default=False, dest="commands") 38 | parser.add_option("-i", help="List IP Addresses", action="store_true", default=False, dest="ip") 39 | 40 | # get arguments 41 | (options, args) = parser.parse_args() 42 | 43 | # if they're trying to access /var/log/auth.log without proper privs, bail 44 | if not os.getuid() is 0 and options.log is None: 45 | print "[-] Please run with SUDO" 46 | sys.exit(1) 47 | 48 | # check if they specified another file 49 | if options.log is not None: 50 | log = options.log 51 | 52 | # parse logs 53 | LOGS = ParseLogs.ParseLogs(log) 54 | if LOGS is None: sys.exit(1) 55 | 56 | # validate the user 57 | if options.user: 58 | if not options.user in LOGS: 59 | print "[-] User \'%s\' is not present in the logs."%options.user 60 | sys.exit(1) 61 | 62 | # tag log location first 63 | print '[!] Log file: ', log 64 | 65 | # output all commands 66 | if options.commands and not options.user: 67 | for i in LOGS: 68 | for comms in LOGS[i].commands: 69 | print "{0}:\t{1}".format(i, comms) 70 | sys.exit(1) 71 | 72 | # output all failures 73 | elif options.fail and not options.user: 74 | for i in LOGS: 75 | for fail in LOGS[i].fail_logs: 76 | print "{0}:\t{1}".format(i, fail) 77 | sys.exit(1) 78 | 79 | # output all logged IP addresses 80 | elif options.ip and not options.user: 81 | for i in LOGS: 82 | for ip in LOGS[i].ips: 83 | print "{0}:\t{1}".format(i, ip) 84 | sys.exit(1) 85 | 86 | # output user-specific commands 87 | if options.commands and options.user: 88 | print "[+] Commands for user \'%s\'"%options.user 89 | for com in LOGS[options.user].commands: 90 | print "\t",com 91 | 92 | # output user-specific success logs 93 | elif options.success and options.user: 94 | print "[+] Successes logs for user \'%s\'"%options.user 95 | for log in LOGS[options.user].succ_logs: 96 | print "\t",log 97 | 98 | # output user-specific failures 99 | elif options.fail and options.user: 100 | print "[+] Failures for user \'%s\'"%options.user 101 | for fail in LOGS[options.user].fail_logs: 102 | print "\t",fail 103 | 104 | # output user-specific ip addresses 105 | elif options.ip and options.user: 106 | print "[+] Logged IPs for user \'%s\'"%options.user 107 | for i in LOGS[options.user].ips: 108 | print "\t", i 109 | 110 | # print out all information regarding specified user 111 | elif options.user is not None: 112 | print "[!] Logs associated with user \'%s\'"%options.user 113 | print '[+] First log: ', LOGS[options.user].first_date() 114 | print '[+] Last log: ', LOGS[options.user].last_date() 115 | print "[!] Failure Logs" 116 | for fail in LOGS[options.user].fail_logs: 117 | print "\t", fail 118 | print "[!] Success Logs" 119 | for succ in LOGS[options.user].succ_logs: 120 | print "\t", succ 121 | print "[!] Associated IPs" 122 | for ip in LOGS[options.user].ips: 123 | print "\t", ip 124 | print "[!] Commands" 125 | for comm in LOGS[options.user].commands: 126 | print "\t", comm 127 | 128 | # dump the full log for the user if specified 129 | if options.fullu and options.user: 130 | print "[!] Full Log" 131 | for log in LOGS[options.user].logs: 132 | print log 133 | 134 | # if they supplied us with an empty user, dump all of the logged users 135 | elif options.user is None: 136 | if len(LOGS) > 0: 137 | for i in LOGS: 138 | print i 139 | --------------------------------------------------------------------------------