kavklaw@llm $ cat log-analysis-guide.md
β±οΈ 30 min read Β· From raw logs to actionable intelligence β detect attacks before they succeed
Most of log analysis is done with command-line tools you already have. Here's what this guide assumes:
grep (pattern matching), awk (field processing), sed (text transformation), sort/uniq/wc (counting and deduplication), pipes (|). If these are unfamiliar, the Bash Scripting cheat sheet covers the essentials.jq β JSON processor for structured logs. Install: sudo apt install jq./var/log/, or a Windows machine with Event Viewer. For practice, sample log files from LogHub work great.Need to investigate something right now? These are the first commands you'd run when triaging a suspicious system β checking for unauthorized access, privilege escalation, and persistence. Linux commands use grep against log files; Windows uses PowerShell's Get-WinEvent to query the Event Log database:
# === LINUX: Quick investigation ===
# Check for failed login attempts (brute force)
grep "Failed password" /var/log/auth.log | tail -20
# Check for successful logins (compromised accounts)
grep "Accepted" /var/log/auth.log | tail -20
# Check for sudo usage (privilege escalation)
grep "sudo:" /var/log/auth.log | tail -20
# Recently modified system files
find /etc -mtime -1 -type f 2>/dev/null
# Check for new user accounts
grep "useradd\|adduser\|new user" /var/log/auth.log
# === WINDOWS: Quick investigation (PowerShell) ===
# Failed logins in the last 24 hours
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4625;StartTime=(Get-Date).AddDays(-1)}
# Successful logins
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624;StartTime=(Get-Date).AddDays(-1)}
# New processes created (requires audit policy)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4688;StartTime=(Get-Date).AddHours(-4)}
# New services installed (common malware persistence)
Get-WinEvent -FilterHashtable @{LogName='System';Id=7045;StartTime=(Get-Date).AddDays(-7)}
That's the quick and dirty version. Now let's build a deep understanding of log analysis that will make you effective at finding attacks.
Logs are timestamped text records that systems generate automatically whenever something happens β a user logs in, a process starts, a file is modified, a network connection is made, or an error occurs. They're the black box recorder of your systems: a chronological trail of every event, stored in files (like /var/log/auth.log) or databases (like Windows Event Logs). Different systems generate different types of logs: authentication logs track who logged in and when, web access logs track every HTTP request to your web server, firewall logs track allowed and blocked network traffic, and application logs track what your software does internally. Without logs, you're blind β you can't detect attacks, investigate incidents, or prove compliance.
The average time to detect a data breach (known as "dwell time") is 194 days (IBM 2024). Organizations with mature log analysis and threat hunting programs detect breaches in weeks, not months. The difference between a minor incident and a catastrophic breach often comes down to how quickly you can find the attacker in your logs.
Logs serve three critical functions:
Linux logs are primarily stored in /var/log/ and managed by either the traditional syslog daemon or systemd's journal. Understanding which log contains what information is fundamental to investigation.
Authentication logs β THE most important log for security. Contains: SSH logins, sudo usage, su commands, PAM events, user creation/deletion, password changes.
/var/log/auth.log # Debian/Ubuntu
/var/log/secure # RHEL/CentOS/Fedora
System logs β general system events. Contains: service start/stop, hardware events, kernel messages, network interface changes.
/var/log/syslog # Debian/Ubuntu
/var/log/messages # RHEL/CentOS
Kernel logs β Contains: hardware errors, kernel module loading (rootkit indicator), firewall messages, storage events.
/var/log/kern.log # Kernel-specific messages
dmesg # Kernel ring buffer (current boot)
Application-specific logs:
/var/log/apache2/ # Apache web server
/var/log/nginx/ # Nginx web server
/var/log/mysql/ # MySQL database
/var/log/postgresql/ # PostgreSQL database
Other important logs:
# Cron logs
/var/log/cron # RHEL/CentOS
grep CRON /var/log/syslog # Debian/Ubuntu
# Package management
/var/log/apt/ # Debian/Ubuntu package installs
/var/log/yum.log # Legacy (RHEL 7 and earlier). Modern RHEL 8+/Fedora use /var/log/dnf.log
# Boot and reboot history
/var/log/boot.log
last -x reboot
On systems using systemd, journalctl provides powerful structured log access:
# View all logs (newest first)
journalctl -r
# View logs from current boot (use -b -1 for previous boot)
journalctl -b
# View logs for a specific service
journalctl -u sshd
journalctl -u nginx
journalctl -u cron
# Filter by time range
journalctl --since "2024-03-15 14:00" --until "2024-03-15 16:00"
journalctl --since "1 hour ago"
journalctl --since yesterday
# Filter by priority (0=emerg to 7=debug)
journalctl -p err # Errors and above
journalctl -p warning # Warnings and above
# Follow logs in real-time (like tail -f)
journalctl -f
journalctl -f -u sshd # Follow specific service
# Output as JSON (great for parsing)
journalctl -o json-pretty
journalctl -u sshd -o json --since "1 hour ago" | jq '.MESSAGE'
# Disk usage of journal
journalctl --disk-usage
# Kernel messages only
journalctl -k
π‘ Pro Tip:journalctlsupports structured data. Use-o jsonand pipe tojqfor powerful filtering:journalctl -u sshd -o json | jq 'select(._COMM == "sshd") | .MESSAGE'. This is far more precise than grepping text logs.
/var/log/auth.log (on Debian/Ubuntu) or /var/log/secure (on RHEL/CentOS) is the primary authentication log. It records SSH logins (success and failure), sudo usage, su commands, PAM events, and user account management. This is the single most important log file for security investigation on Linux.-u sshd filters for the SSH service unit, and --since "1 hour ago" limits the time range. -f follows in real-time (not historical), -b shows since last boot (too broad), and -k shows only kernel messages (SSH runs in userspace).Windows Event Logs are stored in .evtx format and accessed through Event Viewer or PowerShell. They're organized into channels, with three being critical for security:
Microsoft-Windows-PowerShell/Operational) β Script block logging, module logging, transcription.# List all event log channels
Get-WinEvent -ListLog * | Where-Object {$_.RecordCount -gt 0} | Sort-Object RecordCount -Desc
# Query specific event IDs
Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4624,4625
StartTime=(Get-Date).AddDays(-1)
} | Format-Table TimeCreated, Id, Message -Wrap
# Search for specific text in events
Get-WinEvent -FilterHashtable @{LogName='Security'} |
Where-Object {$_.Message -like "*administrator*"} |
Select-Object TimeCreated, Id, Message -First 20
# Export events for analysis
Get-WinEvent -FilterHashtable @{LogName='Security';StartTime=(Get-Date).AddDays(-7)} |
Export-Csv security_events.csv -NoTypeInformation
# Count events by ID (find the noisiest events)
Get-WinEvent -FilterHashtable @{LogName='Security';StartTime=(Get-Date).AddDays(-1)} |
Group-Object Id | Sort-Object Count -Desc | Select-Object Count, Name -First 20
# Sysmon: Process creation with command lines
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational';Id=1} |
Select-Object TimeCreated, @{N='CommandLine';E={$_.Properties[10].Value}} -First 20
These are the Event IDs every analyst must know. Memorize the top 15 β they appear in virtually every investigation.
4624 β Successful Logon. Key fields: Logon Type, Account Name, Source IP.
| Logon Type | Meaning |
|---|---|
| 2 | Interactive (keyboard login) |
| 3 | Network (SMB, net use) |
| 4 | Batch (scheduled task) |
| 5 | Service (service start) |
| 7 | Unlock (screen unlock) |
| 8 | NetworkCleartext (basic HTTP auth) |
| 9 | NewCredentials (RunAs /netonly) |
| 10 | RemoteInteractive (RDP) |
| 11 | CachedInteractive (offline login) |
4625 β Failed Logon. Key fields: Failure Reason, Account Name, Source IP.
| Failure Code | Meaning |
|---|---|
| 0xC0000064 | Username doesn't exist |
| 0xC000006A | Correct username, wrong password |
| 0xC0000234 | Account locked out |
| 0xC0000072 | Account disabled |
| 0xC000006F | Logon outside allowed hours |
Other authentication events:
π‘ Pro Tip: Install Sysmon (System Monitor from Sysinternals) on every Windows system. It provides vastly better logging than native Windows events β including command-line arguments, parent process tracking, network connections, file hash logging, and DNS queries. It's free and transforms your detection capability.
Event ID: 4624
Logon Type: 10
Account Name: admin
Source Network Address: 185.234.72.19
Workstation Name: UNKNOWN
Individual log files on individual systems are useful for investigation, but to detect attacks across your environment, you need centralized log aggregation (collecting all logs in one place) and a SIEM (Security Information and Event Management) system. A SIEM collects, normalizes, and correlates logs from all your systems, then alerts you when it detects suspicious patterns. Think of it as the security operations nerve center: instead of checking logs on 500 individual machines, everything flows into one dashboard where you can search, correlate, and set up detection rules. Popular SIEMs include Splunk (industry standard, powerful but expensive), ELK Stack (Elasticsearch + Logstash + Kibana β open source), Microsoft Sentinel (Azure-native), and QRadar (IBM). The SIEM's killer feature is correlation β connecting a failed login on one server with a successful login on another, then a suspicious process creation on a third, to reveal an attack chain that no individual log would show.
Source Systems β Collectors β Aggregation β SIEM β Alerts
Linux βββββ rsyslog/Filebeat ββ
Windows βββ WEF/Winlogbeat ββββΌβββ Logstash/Kafka βββ ELK/Splunk
Network βββ syslog ββββββββββββ
# === RSYSLOG (Linux log forwarding) ===
# On the client (/etc/rsyslog.d/forward.conf):
*.* @@siem.company.com:514 # TCP forwarding (double @)
*.* @siem.company.com:514 # UDP forwarding (single @)
# Forward only auth logs:
auth,authpriv.* @@siem.company.com:514
# === FILEBEAT (Elastic agent for log shipping) ===
# /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/auth.log
- /var/log/syslog
fields:
log_type: linux_system
output.elasticsearch:
hosts: ["https://elk.company.com:9200"]
username: "filebeat_writer"
password: "${BEAT_PASSWORD}"
# === WINDOWS EVENT FORWARDING (WEF) ===
# Centralize Windows logs without agents
# On collector: wecutil cs subscription.xml
# On sources: winrm quickconfig (GPO-configurable)
# Search for failed logins across all Windows systems
index=wineventlog EventCode=4625
| stats count by src_ip, Account_Name
| sort -count
# Find brute force attempts (>10 failed logins from same IP)
index=wineventlog EventCode=4625
| stats count by src_ip
| where count > 10
| sort -count
# Detect new service installations
index=wineventlog EventCode=7045
| table _time, host, Service_Name, Service_File_Name, Service_Type
# Find cleared security logs
index=wineventlog EventCode=1102
| table _time, host, Account_Name
# Detect lateral movement (Logon Type 3 from unusual sources)
index=wineventlog EventCode=4624 Logon_Type=3
| stats count by src_ip, dest, Account_Name
| sort -count
# Timeline of events on a specific host
index=wineventlog host="DC01"
| sort _time
| table _time, EventCode, Message
ELK (Elasticsearch, Logstash, Kibana) is an open-source SIEM alternative. Kibana is its web interface, and KQL (Kibana Query Language) is how you search logs in it:
# Kibana Query Language (KQL) examples
# Failed logins
event.code: "4625" and event.provider: "Microsoft-Windows-Security-Auditing"
# Successful RDP logins
event.code: "4624" and winlog.event_data.LogonType: "10"
# New processes with command line
event.code: "4688" and process.command_line: *powershell*
# Service installations
event.code: "7045"
# Log cleared
event.code: "1102"
# Sysmon: Network connections to external IPs
event.code: "3" and not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)
You won't always have a SIEM available. Master these command-line tools and you can analyze logs anywhere:
# Find failed SSH logins
grep "Failed password" /var/log/auth.log
# Count failed logins per IP
grep "Failed password" /var/log/auth.log | grep -oP '\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn | head
# Find successful logins outside business hours (before 8am or after 6pm)
grep "Accepted" /var/log/auth.log | grep -E "(0[0-7]|1[89]|2[0-3]):[0-5][0-9]:[0-5][0-9]"
# Search multiple log files recursively
grep -r "error\|failed\|denied" /var/log/ --include="*.log" 2>/dev/null
# Inverse match β find lines that DON'T match (filter noise)
grep -v "CRON\|systemd" /var/log/auth.log
# Context β show lines before and after match
grep -B2 -A5 "Failed password for root" /var/log/auth.log
# Extract specific fields from Apache access log
# Format: IP - - [timestamp] "METHOD /path HTTP/ver" status size
awk '{print $1, $9, $7}' /var/log/apache2/access.log | head
# Output: 10.0.0.1 200 /index.html
# Count requests per IP
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# Find all 4xx and 5xx errors
awk '$9 >= 400 {print $0}' /var/log/apache2/access.log
# Sum bytes transferred per IP
awk '{bytes[$1] += $10} END {for (ip in bytes) print bytes[ip], ip}' \
/var/log/apache2/access.log | sort -rn | head
# Extract timestamps from auth.log
awk '{print $1, $2, $3}' /var/log/auth.log | sort | uniq -c | sort -rn
# Parse JSON logs (common in cloud and containerized environments)
# View pretty-printed JSON
cat app.json | jq '.'
# Extract specific fields
cat app.json | jq '.timestamp, .level, .message'
# Filter by log level
cat app.json | jq 'select(.level == "ERROR")'
# Count events by type
cat app.json | jq -r '.event_type' | sort | uniq -c | sort -rn
# Filter and format
cat app.json | jq 'select(.src_ip != null and .status == 401) | {time: .timestamp, ip: .src_ip, path: .path}'
# Process journalctl JSON output
journalctl -u sshd -o json | jq 'select(.MESSAGE | test("Failed")) | {time: .__REALTIME_TIMESTAMP, msg: .MESSAGE}'
# Top 20 IPs hitting your web server
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Find directory traversal attempts in web logs
grep -E "\.\./|\.\.\\\\|%2e%2e" /var/log/nginx/access.log
# Find SQL injection attempts
grep -iE "(union.*select|or.*1.*=.*1|drop.*table|insert.*into)" /var/log/nginx/access.log
# Find web shell access patterns
grep -E "cmd=|exec=|system\(|passthru|eval\(|shell_exec" /var/log/nginx/access.log
# Extract all unique User-Agent strings
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort -u
# Find large data transfers (potential exfiltration)
awk '$10 > 10000000 {print $1, $10, $7}' /var/log/nginx/access.log | sort -k2 -rn
π‘ Pro Tip: Chain commands together for powerful analysis:zgrep "Failed" /var/log/auth.log* | grep -oP '\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn | head -20. Notezgrepsearches compressed (gzipped) log files too β important because logs rotate and compress daily.
Threat hunting is the proactive search for threats that have evaded existing detection. Unlike alert-driven analysis, hunting starts with a hypothesis and investigates whether the hypothesis is true.
-enc, -EncodedCommand)Invoke-WebRequest, Net.WebClient)# Hypothesis: Attackers are using encoded PowerShell to evade detection
# Data source: Sysmon Event ID 1 (Process Creation), PowerShell Script Block Logging
# Splunk query:
index=sysmon EventCode=1 Image="*powershell.exe"
| where like(CommandLine, "%-enc%") OR like(CommandLine, "%-EncodedCommand%")
| table _time, host, User, ParentImage, CommandLine
# ELK/KQL:
process.name: "powershell.exe" and process.command_line: (*-enc* or *-EncodedCommand* or *FromBase64*)
# Command line (Windows):
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational';Id=1} |
Where-Object {$_.Properties[10].Value -match "enc|EncodedCommand|FromBase64"} |
Select-Object TimeCreated, @{N='User';E={$_.Properties[12].Value}},
@{N='CommandLine';E={$_.Properties[10].Value}}
# Decode found Base64 commands:
echo "VABoAGkAcwAgAGkAcwAgAGEAIAB0AGUAcwB0AA==" | base64 -d
# Or in PowerShell:
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("VABoAGkAcwA..."))
# Pattern: Many failed logins (4625) from same IP, then success (4624)
# Linux: Multiple "Failed password" entries from same IP
# Detection query (Splunk):
index=wineventlog EventCode=4625
| stats count as failures by src_ip
| where failures > 20
| join src_ip [search index=wineventlog EventCode=4624
| stats latest(_time) as success_time by src_ip]
| table src_ip, failures, success_time
# Linux one-liner:
grep "Failed password" /var/log/auth.log | \
awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | \
while read count ip; do
if [ "$count" -gt 20 ]; then
echo "ALERT: $ip had $count failed attempts"
grep "Accepted.*$ip" /var/log/auth.log
fi
done
# Pattern: Logon Type 3 (network) from internal IPs to multiple systems
# Event 4648 (explicit credential usage) across multiple destinations
# Detection: Same account logging into many systems in short timeframe
index=wineventlog EventCode=4624 Logon_Type=3
| bin _time span=1h
| stats dc(dest) as unique_targets by _time, Account_Name, src_ip
| where unique_targets > 5
| sort -unique_targets
# PsExec detection: Service installation (7045) with PSEXESVC
index=wineventlog EventCode=7045 Service_Name="PSEXESVC"
| table _time, host, Service_File_Name
# Pattern: Large outbound data transfers, DNS tunneling, unusual protocols
# Large HTTP uploads:
awk '$10 > 50000000 {print $1, $7, $10}' /var/log/nginx/access.log
# (POST requests with large body sizes)
# DNS tunneling (unusually long DNS queries):
# In Zeek/Bro dns.log:
awk -F'\t' 'length($10) > 50 {print $10}' dns.log | sort | uniq -c | sort -rn
# Sysmon network connections to unusual destinations:
index=sysmon EventCode=3 NOT (DestinationIp=10.* OR DestinationIp=172.16.* OR DestinationIp=192.168.*)
| stats sum(BytesSent) as total_bytes by Image, DestinationIp
| where total_bytes > 100000000
| sort -total_bytes
MITRE ATT&CK is a publicly available knowledge base of real-world attacker techniques, organized by tactics (what attackers want to achieve) and techniques (how they do it). Each technique has an ID (like T1078) and maps to specific log sources that can detect it. It's the common language security teams use to describe attacks and build detections:
| Technique | Log Sources | What to Hunt For |
|---|---|---|
| T1078 β Valid Accounts | 4624/4625, 4648 | Logins from unusual locations, impossible travel |
| T1059 β Command Interpreter | Sysmon 1, PowerShell 4104 | Encoded commands, unusual script execution |
| T1021 β Remote Services | 4624 Type 10/3, auth.log | Connections from unusual IPs, off-hours access |
| T1053 β Scheduled Task/Job | 4698, Sysmon 1 (schtasks) | Tasks running from temp dirs, SYSTEM tasks |
| T1543 β System Process | 7045, Sysmon 1 (sc.exe) | Services with random names, paths in Temp |
| T1003 β Credential Dumping | Sysmon 10, 4688 | Processes accessing LSASS, Mimikatz signatures |
| T1070 β Log Clearing | 1102, 104 | Any log clearing events (critical red flag) |
| T1071 β C2 Protocol | Sysmon 3, proxy/DNS logs | Beaconing patterns, unusual User-Agents, DNS tunneling |
Here's a practical methodology for log-based investigation of a suspected compromise:
βββββββββββββββββββββββββββββββββββββββββββ
β INCIDENT INVESTIGATION FLOW β
ββββββββββββββββββββ¬βββββββββββββββββββββββ
βΌ
Step 1: TIMELINE ββββ When did it start/end?
β Work backwards from the alert
βΌ
Step 2: SCOPE βββββββ How far did they get?
β Search compromised account + IP
β across ALL log sources
βΌ
Step 3: INITIAL βββββ How did they get in?
ACCESS Auth logs, web logs, email, VPN
β
βΌ
Step 4: ACTIONS βββββ What did they do?
β Process creation, file access,
β network connections, persistence
βΌ
Step 5: IMPACT ββββββ What's the damage?
β Data accessed/exfiltrated?
β Accounts created/modified?
βΌ
Step 6: IOCs ββββββββ Extract indicators
IPs, domains, hashes β hunt elsewhere
β Mistake #1: Not enabling audit policies.
Windows doesn't log most security events by default. You must enable Advanced Audit Policies, Process Creation auditing (with command-line logging), and PowerShell Script Block Logging. Without these, your logs are nearly useless for investigation.
β Mistake #2: Not centralizing logs.
If logs only exist on individual systems, an attacker who compromises a system can delete the logs. Centralize logs to a dedicated SIEM or log server that the attacker can't access.
β Mistake #3: Insufficient log retention.
The average dwell time is 194 days. If you only keep 30 days of logs, you can't investigate breaches that happened months ago. Retain security logs for at least 1 year.
β Mistake #4: Ignoring UTC timestamps.
When correlating logs from multiple systems, timezone confusion creates false timelines. Standardize all logging to UTC. Configure NTP to keep system clocks synchronized.
β Mistake #5: Not installing Sysmon.
Native Windows logging has significant gaps β no command-line arguments by default, no parent process tracking, no network connection logging. Sysmon fills these gaps and is free. There's no excuse not to deploy it.
β Mistake #6: Alert fatigue from too many rules.
Having 10,000 SIEM rules that generate thousands of alerts daily means nothing gets investigated. Start with high-fidelity rules for critical events (log clearing, new admin accounts, service installs) and expand gradually.