courses

Network Log Forensics

Network logs are the written record of who spoke to whom, when, on which port, and with what outcome. Unlike a packet capture, logs are always-on, compact, and frequently retained for weeks or months — making them the most common source of network evidence in enterprise investigations. This page covers how to read, query, and correlate the major log types encountered in enterprise environments.

For packet-level acquisition and analysis, see Network Forensics.


Foundations: Time, Attribution, and Evidentiary Limits

Before analyzing any log, establish these constraints. Skipping them is the most common source of attribution errors.

Timestamps and Timezone

Log timestamps mean nothing without a timezone. Enterprise logs are frequently a mix of UTC, local time, and offset-from-UTC. A three-hour discrepancy between a firewall log in UTC and a Windows event log in US/Eastern (UTC-5) during winter will appear as a two-hour gap in a timeline.

Rules:

IP Attribution and NAT

An IP address in a log is not an identity. In a network with NAT:

Log Retention and Gaps

Logs that do not exist cannot be used as evidence. Before concluding “there was no connection,” confirm:

Evidentiary Integrity

Logs on a live server are not forensically pristine. For investigation purposes:


Correlation Primer

The power of network logs comes from pivoting across sources using shared observables. The three most common pivots are:

Pivot From → To
IP + time Firewall deny → DHCP lease → user identity
Hostname DNS query → proxy log → destination IP
Session DHCP lease → VPN auth → AD login

General investigation workflow:

  1. Start with a known indicator (IP, hostname, username, time of incident).
  2. Expand through DHCP to get MAC and time window.
  3. Expand through DNS to get hostnames contacted.
  4. Expand through firewall/proxy to get destinations and volumes.
  5. Enrich with Suricata/IDS alerts and NetFlow for behavioral patterns.
  6. Reconstruct a timeline ordered by UTC timestamp.

A worked end-to-end scenario using all of these sources is at the bottom of this page.


DHCP Logs

DHCP logs are the bridge between an IP address (seen in all other logs) and a physical or virtual machine identity (MAC address, hostname). They are almost always the first log consulted when attributing an IP to a device.

ISC dhcpd (Linux)

Default log path: /var/log/syslog or /var/log/dhcpd.log

May 12 09:14:22 dhcpd[1234]: DHCPREQUEST for 192.168.8.215 from b8:27:eb:aa:11:22 (rpi-device) via eth0
May 12 09:14:22 dhcpd[1234]: DHCPACK on 192.168.8.215 to b8:27:eb:aa:11:22 (rpi-device) via eth0
May 12 09:14:22 dhcpd[1234]: uid lease 192.168.8.215 for client b8:27:eb:aa:11:22 is assigned with a lease time of 86400 seconds
May 13 09:14:22 dhcpd[1234]: DHCPEXPIRE 192.168.8.215 to b8:27:eb:aa:11:22

Key fields:

Field Forensic value
Timestamp When the IP was assigned
DHCPACK Confirmed assignment (more reliable than DISCOVER/OFFER)
DHCPEXPIRE When the lease ended — defines the attribution window
MAC address Device identity (may be randomized on mobile)
Hostname Client-supplied, not authenticated; treat as advisory
Lease time Calculate when the same IP could be reassigned

Querying the lease for a given IP:

grep 'DHCPACK on 192.168.8.215' /var/log/syslog | awk '{print $1,$2,$3,$8,$10,$11}'

Windows DHCP Server

Windows DHCP logs live in C:\Windows\System32\dhcp\ as DhcpSrvLog-Mon.log etc. Format is CSV-like:

ID,Date,Time,Description,IP Address,Host Name,MAC Address
10,05/12/25,09:14:22,Assign,192.168.8.215,LAPTOP-7XQ,b8-27-eb-aa-11-22
12,05/13/25,09:14:22,Release,192.168.8.215,LAPTOP-7XQ,b8-27-eb-aa-11-22

Event ID 10 = new lease; 11 = renew; 12 = release; 15 = deny.

Forensic pitfalls:


DNS Logs

DNS logs show every name resolution attempted from within the network. They reveal:

BIND Query Log

Enable in /etc/named.conf:

logging {
    channel query_log {
        file "/var/log/named/query.log" versions 5 size 50m;
        severity dynamic;
        print-time yes;
        print-category yes;
    };
    category queries { query_log; };
};

Sample output:

12-May-2025 09:15:01.234 client 192.168.8.215#54321 (example.com): query: example.com IN A + (192.168.8.1)
12-May-2025 09:15:02.110 client 192.168.8.215#54322 (c2server.bad.example): query: c2server.bad.example IN A + (192.168.8.1)
12-May-2025 09:15:03.001 client 192.168.8.215#54323 (aGVsbG8gd29ybGQ.tunnel.attacker.io): query: aGVsbG8gd29ybGQ.tunnel.attacker.io IN TXT + (192.168.8.1)

Key fields:

Field Meaning
Timestamp Query time
Client IP Querying host
Query name Hostname being resolved
Query type A, AAAA, MX, TXT, PTR — type matters for detecting tunneling
Resolver IP Which upstream resolver was used

Windows DNS Debug Log

Enable in DNS Manager → Server Properties → Debug Logging. Log writes to %systemroot%\System32\dns\dns.log.

5/12/2025 9:15:01 AM 0D40 PACKET  Snd UDP 192.168.8.215  R Q [8081   D    NOERROR] A       (7)example(3)com(0)

DNS tunneling indicators:

Indicator Description
Unusually long subdomains (>50 chars) Data encoded in labels
High query rate to a single domain Polling a C2 or exfiltrating
TXT queries from non-mail hosts TXT is the preferred tunnel response type
High entropy in subdomain labels Base64/hex-encoded content
PTR queries in non-standard ranges Reconnaissance

Finding long query names (DNS tunneling triage):

awk '{print length($8), $8}' /var/log/named/query.log | sort -rn | head -20

Counting queries per source to detect polling:

grep 'query:' /var/log/named/query.log | awk '{print $4}' | cut -d# -f1 | sort | uniq -c | sort -rn | head -20

Evidentiary limits: Long subdomains and TXT queries have legitimate uses (SPF, DKIM, ACME challenges, split-horizon DNS). Correlation with NetFlow volume and timing is required before concluding tunneling is present.


Firewall and NAT Logs

Firewall logs record connection attempts — allowed and denied — at the network boundary. NAT translation logs are essential for mapping a public IP seen in an external log back to the internal host that originated the traffic.

iptables / nftables (Linux)

iptables logging is configured per rule:

iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4

Syslog output:

May 12 09:20:01 gw kernel: IPTABLES-DROP: IN=eth0 OUT= MAC=00:11:22:33:44:55:66:77:88:99:aa:bb:08:00
  SRC=203.0.113.42 DST=192.168.8.215 LEN=44 TOS=0x00 TTL=51 ID=54321 PROTO=TCP SPT=44567 DPT=22 WINDOW=65535 SYN

Key fields:

Field Meaning
IN/OUT Interface direction
SRC/DST Source and destination IP
SPT/DPT Source and destination port
PROTO Protocol (TCP, UDP, ICMP)
SYN/ACK/RST TCP flags
Prefix Rule chain context (e.g., DROP, ACCEPT)

nftables uses the same syslog format when log is used in rules.

Palo Alto Traffic Log

Palo Alto logs are CSV/syslog. Key fields in a traffic log entry:

FUTURE_USE,RECEIVE_TIME,SERIAL,TYPE,THREAT_CONTENT_TYPE,FUTURE_USE,
GENERATED_TIME,SRC_IP,DST_IP,NAT_SRC_IP,NAT_DST_IP,RULENAME,
SRC_USER,DST_USER,APPLICATION,VSYS,SRC_ZONE,DST_ZONE,INBOUND_IF,OUTBOUND_IF,
LOG_FWDING,FUTURE_USE,SESSION_ID,REPEAT_COUNT,SRC_PORT,DST_PORT,
NAT_SRC_PORT,NAT_DST_PORT,FLAGS,PROTOCOL,ACTION,BYTES,...,ELAPSED_TIME,...

The ACTION field is allow, deny, drop, reset-client, or reset-server. The APPLICATION field reflects Palo Alto’s App-ID classification, not just port numbers.

Identifying denied outbound connections (lateral movement attempt, C2 blocked):

grep ',deny,' palo_traffic.csv | awk -F',' '{print $8,$9,$25,$26,$31}' | sort | uniq -c | sort -rn

NAT Translation Logs

When traffic traverses a NAT device, the public IP visible in external logs corresponds to the NAT device, not the internal host. The NAT log records the translation:

May 12 09:21:00 fw1 kernel: NAT: src=192.168.8.215:54321 -> 203.0.113.1:61234 dst=198.51.100.5:443

Without this log, you cannot attribute an external connection to a specific internal host. For investigations involving external victims or authorities, the NAT translation log is often subpoenaed.

Forensic pitfalls:


Web Proxy and HTTP Logs

Enterprise web proxies intercept outbound HTTP/HTTPS and log URL-level detail. For HTTPS, a decrypting (SSL-inspection) proxy provides full URL and sometimes response content; a non-decrypting proxy logs only the CONNECT destination hostname.

Squid Access Log

Default format (/var/log/squid/access.log):

1747041001.234    312 192.168.8.215 TCP_MISS/200 48291 GET http://example.com/download.zip - DIRECT/93.184.216.34 application/zip
1747041002.001      0 192.168.8.118 TCP_DENIED/403 4096 CONNECT badsite.example.com:443 - NONE/- -

Fields: timestamp(epoch) elapsed client action/code bytes method url user peer/dest content-type

Action code Meaning
TCP_MISS Cache miss, fetched from origin
TCP_HIT Served from Squid cache
TCP_DENIED Blocked by ACL
CONNECT HTTPS tunnel (non-decrypting proxy)

Filtering for large downloads (potential exfiltration or dropper staging):

awk '$5 > 10000000 {print $3,$6,$7,$5}' /var/log/squid/access.log | sort -k4 -rn

W3C Extended Log Format (IIS / Blue Coat / Zscaler)

W3C format is used by IIS, many commercial proxies, and cloud web gateways:

#Version: 1.0
#Date: 2025-05-12 09:22:00
#Fields: date time c-ip cs-method cs-host cs-uri-stem cs-uri-query sc-status sc-bytes cs(User-Agent)
2025-05-12 09:22:01 192.168.8.215 GET update.example.com /update/client.exe - 200 4194304 curl/7.88.1
2025-05-12 09:22:15 192.168.8.118 CONNECT c2.badactor.io:443 - - 200 8192 -

Forensic note on CONNECT: A CONNECT log entry with status 200 means the proxy established a tunnel but did not inspect the content. You know the destination but not what was transferred. A CONNECT entry followed by unusually large bytes transferred may warrant packet capture or SSL inspection review.

Evidentiary limits:


VPN Logs

VPN logs record when remote users authenticate and what IP they receive. They are essential for attributing off-premises connections.

OpenVPN

Default log entries (server-side):

Mon May 12 09:30:00 2025 192.0.2.77:51234 TLS: Initial packet from [AF_INET]192.0.2.77:51234, sid=abc123
Mon May 12 09:30:02 2025 192.0.2.77:51234 VERIFY OK: depth=0, CN=jsmith
Mon May 12 09:30:02 2025 192.0.2.77:51234 [jsmith] Peer Connection Initiated with [AF_INET]192.0.2.77:51234
Mon May 12 09:30:02 2025 jsmith/192.0.2.77:51234 MULTI: Learn: 10.8.0.6/32 -> jsmith/192.0.2.77:51234
Mon May 12 09:30:02 2025 jsmith/192.0.2.77:51234 MULTI_sva: pool returned IPv4=10.8.0.6
Mon May 12 09:30:45 2025 jsmith/192.0.2.77:51234 Connection reset, restarting [0]
Mon May 12 09:30:45 2025 jsmith/192.0.2.77:51234 SIGUSR1[soft,connection-reset] received, client-instance restarting

Key fields:

Field Forensic value
Client external IP (192.0.2.77) Public IP of the remote user
Common Name (jsmith) Certificate identity — more reliable than password-based username
Assigned internal IP (10.8.0.6) IP that will appear in internal logs during the session
Session start/end times Defines the attribution window

Finding sessions for a user:

grep 'jsmith' /var/log/openvpn.log | grep -E 'Initiated|Connection reset'

Geolocation anomaly triage: If a user’s VPN connection originates from an unexpected country or ASN, correlate against their normal login pattern. Note: VPN services, mobile carrier NATs, and cloud VM exit points make geolocation unreliable as a sole indicator.

Windows RRAS / Always-On VPN

Event IDs in Windows Event Log (Security):

Event ID Meaning
20272 IKE security association established (VPN connected)
20274 VPN connection established
20275 VPN connection disconnected
20276 Authentication failed

RADIUS and 802.1X Logs

RADIUS authenticates network access: wired 802.1X, wireless WPA2-Enterprise, and VPN. RADIUS logs identify which user authenticated from which device at what time, enabling user-to-port and user-to-SSID attribution.

FreeRADIUS

Default log (/var/log/freeradius/radius.log):

Mon May 12 09:35:00 2025 : Auth: (1234) Login OK: [jsmith] (from client ap-lobby port 0 cli b8-27-eb-aa-11-22)
Mon May 12 09:47:12 2025 : Auth: (1235) Login incorrect: [baduser] (from client sw-floor3 port 48 cli 00-11-22-33-44-55)

Fields: timestamp, event type, username, NAS client, port (switch port or AP), calling-station-id (client MAC).

Key forensic questions RADIUS answers:

Detecting authentication failures (password spray):

grep 'Login incorrect' /var/log/freeradius/radius.log | \
  awk '{print $9}' | tr -d '[]' | sort | uniq -c | sort -rn | head -20

Wireless Controller / AP Logs

Enterprise wireless controllers (Cisco WLC, Aruba, Juniper Mist) log association events that RADIUS does not capture:

These logs are essential when a physical location for a network event is needed.

Evidentiary limits:


NetFlow and IPFIX

NetFlow (Cisco) and IPFIX (IETF standard) are flow-level summaries exported by routers and switches. They record who spoke to whom, how much data was transferred, and for how long — without capturing payload content.

A flow record represents one conversation:

Field Description
src_addr Source IP
dst_addr Destination IP
src_port Source port
dst_port Destination port
protocol IP protocol number
packets Packet count
bytes Byte count
start / end Flow start and end timestamps
tcp_flags OR of all TCP flags in the flow

nfdump queries NetFlow files exported by nfcapd:

# Top talkers by bytes in a time window
nfdump -r /var/cache/nfdump/ -t '2025/05/12.09:00:00-2025/05/12.10:00:00' \
  -s record/bytes -n 20

# All flows from a suspicious host
nfdump -r /var/cache/nfdump/ 'src ip 192.168.8.215' -o long

# Large outbound transfers (potential exfiltration)
nfdump -r /var/cache/nfdump/ 'bytes > 100000000 and dst net not 192.168.0.0/16' -o long

Beaconing Detection

Malware C2 beaconing produces periodic small flows to the same destination at regular intervals. Characteristics:

Characteristic Typical beacon Typical legitimate traffic
Inter-flow interval Regular (e.g., every 60s ± jitter) Irregular
Bytes per flow Small and consistent Variable
Destination Fixed single IP or small pool Diverse
Duration of activity Hours to days Minutes to hours
# Find flows to a suspected C2 at regular intervals
nfdump -r /var/cache/nfdump/ 'dst ip 203.0.113.99' -o 'fmt:%ts %td %byt' | \
  awk '{print $1}' | sort | uniq -c

Evidentiary limits: NetFlow does not capture payload, so you can prove a connection existed and how large it was, but not what was transferred. Exfiltration of 100MB and download of a software update are indistinguishable from NetFlow alone. MIME type from a proxy log or file signatures from a PCAP are needed for content attribution.


Suricata EVE JSON

Suricata is a network IDS/IPS that writes machine-readable JSON events to a single log file called eve.json. Unlike traditional alert-only IDS logs, Suricata also logs protocol metadata (DNS queries, HTTP requests, TLS SNI) regardless of whether a signature matches.

Default path: /var/log/suricata/eve.json

Each event has a event_type field:

event_type Content
alert Signature match with rule metadata
dns Every DNS query/response observed
http HTTP request/response metadata
tls TLS handshake metadata (SNI, cert subject, JA3)
flow Completed TCP/UDP flow record
fileinfo File observed in a flow (hash, name, MIME)
ssh SSH client/server version strings
smtp SMTP envelope metadata

Querying EVE with jq

All alerts:

jq 'select(.event_type == "alert") | {ts: .timestamp, src: .src_ip, dst: .dest_ip, sig: .alert.signature}' \
  /var/log/suricata/eve.json

HTTP requests from a host:

jq 'select(.event_type == "http" and .src_ip == "192.168.8.215") |
  {ts: .timestamp, method: .http.http_method, url: (.http.hostname + .http.url), ua: .http.http_user_agent}' \
  /var/log/suricata/eve.json

TLS SNI seen from a host (HTTPS destinations without decryption):

jq 'select(.event_type == "tls" and .src_ip == "192.168.8.215") |
  {ts: .timestamp, sni: .tls.sni, ja3: .tls.ja3.hash}' \
  /var/log/suricata/eve.json

JA3 fingerprint correlation: JA3 is a hash of TLS client hello parameters. Malware families often have distinctive JA3 hashes. Suricata logs the JA3 hash in every TLS event, allowing correlation even when the SNI or certificate changes.

Files observed (dropper staging, C2 download):

jq 'select(.event_type == "fileinfo") |
  {ts: .timestamp, src: .src_ip, dst: .dest_ip, name: .fileinfo.filename, md5: .fileinfo.md5, size: .fileinfo.size}' \
  /var/log/suricata/eve.json

Evidentiary limits:


SMTP and Mail Server Logs

Mail server logs record the routing metadata for every message: envelope sender, recipient, relay hops, delivery status, and attachment count. They do not typically record message body content unless content inspection is explicitly configured.

Postfix

Postfix logs to syslog (usually /var/log/mail.log). Each message produces multiple entries tied by a queue ID:

May 12 09:40:01 mx1 postfix/smtpd[4321]: connect from mail.sender.example[203.0.113.5]
May 12 09:40:01 mx1 postfix/smtpd[4321]: A1B2C3D4E5F6: client=mail.sender.example[203.0.113.5]
May 12 09:40:02 mx1 postfix/cleanup[4322]: A1B2C3D4E5F6: message-id=<xyz@sender.example>
May 12 09:40:02 mx1 postfix/qmgr[1000]: A1B2C3D4E5F6: from=<alice@sender.example>, size=204800, nrcpt=1
May 12 09:40:03 mx1 postfix/smtp[4323]: A1B2C3D4E5F6: to=<bob@company.com>, relay=mail.company.com[10.0.0.25]:25, delay=2, status=sent (250 OK)

Reconstructing message routing by queue ID:

grep 'A1B2C3D4E5F6' /var/log/mail.log

Microsoft Exchange Message Tracking

Exchange writes CSV tracking logs to C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\MessageTracking\. Fields include:

date-time, client-ip, client-hostname, server-ip, source, event-id, internal-message-id, message-id, recipient-address, recipient-status, total-bytes, recipient-count, subject

Event IDs of forensic interest:

Event ID Meaning
RECEIVE Server received a message
SEND Server sent a message outbound
DELIVER Message delivered to mailbox
FAIL Delivery failed
REDIRECT Message redirected (mail forwarding rule)

Detecting auto-forwarding rules (data exfiltration via email):

grep 'REDIRECT' MessageTracking*.log | awk -F',' '{print $7,$10}' | sort | uniq -c | sort -rn

Persistent REDIRECT events for a single internal sender routing messages to an external address are a strong indicator of a compromised mailbox with an exfiltration forwarding rule.


SSH Logs

SSH authentication events are recorded by PAM and the sshd daemon to syslog. On most Linux systems this is /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL/CentOS).

Authentication Events

May 12 09:45:01 server sshd[5678]: Failed password for jsmith from 203.0.113.42 port 44567 ssh2
May 12 09:45:03 server sshd[5678]: Failed password for jsmith from 203.0.113.42 port 44568 ssh2
May 12 09:45:05 server sshd[5679]: Accepted publickey for jsmith from 10.8.0.6 port 52341 ssh2: RSA SHA256:abc123...
May 12 09:45:05 server sshd[5679]: pam_unix(sshd:session): session opened for user jsmith by (uid=0)
May 12 09:45:45 server sshd[5679]: pam_unix(sshd:session): session closed for user jsmith

Key log events:

Pattern Forensic significance
Failed password Brute force, credential spray
Invalid user Scanning for valid usernames
Accepted publickey Successful key-based login
Accepted password Successful password login
session opened / session closed Session duration
Connection closed without session opened Port scan or early disconnect

Count failed attempts per source (brute force triage):

grep 'Failed password' /var/log/auth.log | \
  awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20

Find successful logins after failures from the same source:

awk '/Failed password/{fail[$NF]++} /Accepted/{if(fail[$NF]>0) print "SUCCESS after",fail[$NF],"failures from",$NF}' \
  /var/log/auth.log

Evidentiary limits:


Worked Investigation Scenario

Scenario: The security team receives an alert that 192.168.8.157 is serving files over unusual ports. Determine what happened, which device was involved, and what was transferred.

Step 1 — DHCP: identify the device

grep 'DHCPACK on 192.168.8.157' /var/log/syslog
# → 192.168.8.157 assigned to MAC aa:bb:cc:dd:ee:ff (hostname: fw-server) at 08:00:00
# → Lease valid for 86400 seconds (expires 09:00:00 next day)

Attribution: 192.168.8.157 = fw-server, MAC aa:bb:cc:dd:ee:ff during this window.

Step 2 — Firewall: what ports were open?

grep '192.168.8.157' /var/log/syslog | grep 'ACCEPT' | awk '{print $26}' | sort -u
# → DPT=12599 through DPT=12619 accepted inbound from 192.168.8.215

Finding: fw-server accepted connections on 21 ephemeral ports from 192.168.8.215.

Step 3 — Suricata HTTP events: what was requested?

jq 'select(.event_type == "http" and .dest_ip == "192.168.8.157") |
  {ts: .timestamp, src: .src_ip, url: .http.url, bytes: .http.length}' eve.json | head -40
# → GET /download?name=firmware.bin&offset=<N>&size=<M> — dozens of chunked requests

Finding: 192.168.8.215 downloaded firmware.bin in chunks across 21 parallel TCP connections.

Step 4 — DHCP: identify the client

grep 'DHCPACK on 192.168.8.215' /var/log/syslog
# → MAC b8:27:eb:aa:11:22 (hostname: rpi-device) — Raspberry Pi OUI

Attribution: 192.168.8.215 = an IoT/embedded device (Raspberry Pi), rpi-device.

Step 5 — NetFlow: total bytes transferred

nfdump -r /var/cache/nfdump/ 'src ip 192.168.8.215 and dst ip 192.168.8.157' -s record/bytes
# → Total: ~4.3 GB over 45 minutes

Finding: 4.3 GB of firmware data was transferred to the Raspberry Pi.

Step 6 — DNS: did rpi-device contact any external hosts?

grep '192.168.8.215' /var/log/named/query.log | awk '{print $8}' | sort | uniq -c | sort -rn
# → wttr.in, httpbin.org, ifconfig.co, captive.apple.com (all consistent with IoT device startup)

Finding: No anomalous external DNS queries. The firmware retrieval was entirely internal.

Summary: A Raspberry Pi (rpi-device, 192.168.8.215) downloaded approximately 4.3 GB of firmware from fw-server (192.168.8.157) using a custom chunked HTTP download protocol on ports 12599–12619. All traffic was internal. The activity appears to be a legitimate firmware update mechanism, but the custom download server and large number of parallel connections warrant further review of fw-server’s purpose.


Log Source Quick Reference

Log What it proves What it cannot prove
DHCP IP → MAC → hostname at time T User identity; MAC authenticity
DNS Host queried a domain What was returned; what was done with it
Firewall Connection was allowed/denied Whether the allowed connection succeeded at the app layer
NAT Internal IP that used a public IP:port Content of the connection
Proxy URL visited; bytes transferred Content if HTTPS without SSL inspection
VPN User authenticated; received IP What the user did after connecting
RADIUS Username authenticated to this network segment That the authenticated user was present at the device
NetFlow Volume and duration of a conversation Content; application
Suricata EVE Protocol metadata; rule matches That an attack succeeded; traffic on unmonitored segments
SMTP Message routing and envelope Message body (unless content inspection enabled)
SSH Auth events; session open/close Commands executed; tunneled traffic