Log analyzer

The goal

I wanted to see what a real defender spends a lot of time doing: reading through logs to spot failed logins and patterns that look like an attack, rather than one-off mistakes.

What you need

Python 3, and about two hours. No extra libraries, this only uses re and collections, both built in.

How I built it

The script reads a log file line by line and uses a regular expression to pull out lines matching a “Failed password” pattern, capturing the date, the username that was tried, and the source IP. It then groups all the failed attempts by IP address. If one IP shows up 5 or more times, it gets flagged as a possible brute-force attempt, since a real person mistyping their password rarely fails that many times in a row.

import re
from collections import defaultdict

FAILED_LOGIN_PATTERN = re.compile(
r'(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*?’
r’Failed password for (?:invalid user )?(?P\S+) from (?P\d+.\d+.\d+.\d+)’
)

BRUTE_FORCE_THRESHOLD = 5

def parse_log(lines):
events = []
for line in lines:
match = FAILED_LOGIN_PATTERN.search(line)
if match:
events.append((match.group(“date”), match.group(“user”), match.group(“ip”)))
return events

def summarize(events):
by_ip = defaultdict(list)
by_user = defaultdict(int)
for date, user, ip in events:
by_ip[ip].append((date, user))
by_user[user] += 1
return by_ip, by_user

Run it with:

python3 log_analyzer.py

or point it at a real file:

python3 log_analyzer.py yourfile.log

What broke

My first regular expression only matched lines in exactly one log format. The moment I tested it against a slightly different style, like one that said Failed password for invalid user guest from ... instead of Failed password for guest from ..., it missed every line. I had to add an optional (?:invalid user )? part to the pattern to cover both cases.

What I learned

A huge part of log analysis is just getting the pattern-matching right for whatever log format you’re actually looking at, real logs are messier and less consistent than the clean examples in tutorials. Counting repeated failures by source is a simple idea, but it catches something a human skimming a long log file would likely miss.

Try it yourself

Add a check for failed attempts against multiple different usernames from the same IP in a short time window, which is a stronger brute-force signal than a high count against one username alone. You could also add a simple check for successful logins that happen right after a string of failures, which can mean an attacker finally guessed correctly.

The full code is on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *