cyber-security-resources/python_ruby_and_bash/parsing_auth_log/ParseLogs.py
2020-12-13 20:28:02 -05:00

159 lines
4.6 KiB
Python

import gzip
import re
#
# ParseLogs.py
# Parsing component of Logalyzer. Original: https://github.com/hatRiot/logalyzer
# Converted to python3.6 by @programmerchad
#
# log object
# Stuck into a dictionary by user:Log, where log houses
# logs, fails, successes, logged IPs, and commands used
class Log:
# dump date of first log
def first_date(self):
if len(self.logs) > 0:
date = None
i = 0
# sometimes the first few aren't right, so look
# until we find one
while i < len(self.logs) and date is None:
date = ParseDate(self.logs[i])
i += 1
return date
# dump date of last log
def last_date(self):
if len(self.logs) > 0:
return ParseDate(self.logs[len(self.logs) - 1])
def __init__(self, usr):
self.usr = usr
self.logs = []
self.fail_logs = []
self.succ_logs = []
self.ips = []
self.commands = []
# parse user from various lines
def ParseUsr(line):
usr = None
if "Accepted password" in line:
usr = re.search(r'(\bfor\s)(\w+)', line)
elif "sudo:" in line:
usr = re.search(r'(sudo:\s+)(\w+)', line)
elif "authentication failure" in line:
usr = re.search(r'USER=\w+', line)
elif "for invalid user" in line:
usr = re.search(r'(\buser\s)(\w+)', line)
if usr is not None:
return usr.group(2)
# parse an IP from a line
def ParseIP(line):
ip = re.search(r'(\bfrom\s)(\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)', line)
if ip is not None:
return ip.group(2)
# parse a date from the line
def ParseDate(line):
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)
if date is not None:
return date.group(0)
# parse a command from a line
def ParseCmd(line):
# parse command to end of line
cmd = re.search(r'(\bCOMMAND=)(.+?$)', line)
if cmd is not None:
return cmd.group(2)
# begin parsing the passed LOG
def ParseLogs(log):
# initialize the dictionary
logs = {}
# parse the log
f = None
try:
f = gzip.open(log, 'r') if '.gz' in log else open(log, 'r')
log = f.read()
except Exception as e:
print('[-] Error opening \'%s\': %s' % (log, e))
return None
finally:
if f is not None:
f.close()
for line in log.split('\n'):
# match a login
if "Accepted password for" in line:
usr = ParseUsr(line)
# add 'em if they don't exist
if usr not in logs:
logs[usr] = Log(usr)
ip = ParseIP(line)
# set info
if ip not in logs[usr].ips:
logs[usr].ips.append(ip)
logs[usr].succ_logs.append(line.rstrip('\n'))
logs[usr].logs.append(line.rstrip('\n'))
# match a failed login
elif "Failed password for" in line:
# parse user
usr = ParseUsr(line)
if usr not in logs:
logs[usr] = Log(usr)
ip = ParseIP(line)
if ip not in logs[usr].ips:
logs[usr].ips.append(ip)
logs[usr].fail_logs.append(line.rstrip('\n'))
logs[usr].logs.append(line.rstrip('\n'))
# match failed auth
elif ":auth): authentication failure;" in line:
# so there are three flavors of authfail we care about;
# su, sudo, and ssh. Lets parse each.
usr = re.search(r'(\blogname=)(\w+)', line)
if usr is not None:
usr = usr.group(2)
# parse a fail log to ssh
if "(sshd:auth)" in line:
# ssh doesn't have a logname hurr
usr = ParseUsr(line)
if usr not in logs:
logs[usr] = Log(usr)
logs[usr].ips.append(ParseIP(line))
# parse sudo/su fails
else:
if usr not in logs:
logs[usr] = Log(usr)
logs[usr].fail_logs.append(line.rstrip('\n'))
logs[usr].logs.append(line.rstrip('\n'))
# match commands
elif "sudo:" in line:
# parse user
usr = ParseUsr(line)
if usr not in logs:
logs[usr] = Log(usr)
cmd = ParseCmd(line)
# append the command if it isn't there already
if cmd is not None:
if cmd not in logs[usr].commands:
logs[usr].commands.append(cmd)
logs[usr].logs.append(line.rstrip('\n'))
return logs