Web System Forensics: How to Reconstruct an Attack After It Happens
Most security content focuses on prevention. But what happens when something slips through? What happens when you open your server logs at 2 AM and realize something has been happening for the last six hours?
Table of contents:
- What Web System Forensics Actually Means
- Phase 1 — Preserve Before You Investigate
- Phase 2 — Timeline Reconstruction
- Phase 3 — Attack Vector Analysis
- Phase 4 — Scope Determination
- Phase 5 — Attribution and Pattern Analysis
- Phase 6 — Remediation with Forensic Discipline
- The Incident Report
- Building a Forensic-Ready Infrastructure
- Final Thought
What Web System Forensics Actually Means
Web system forensics applies the discipline of digital forensics to web infrastructure — servers, applications, databases, and the logs that connect them.
The goal is not just to confirm that an attack happened. The goal is to answer five questions with precision:
- What was the nature of the attack?
- When did it begin, and when did it end?
- How did the attacker operate — what was the method, the sequence, the tooling?
- What was accessed, modified, or exfiltrated?
- Where did the attack originate, and are there residual threats?
Without answering all five, your incident response is incomplete — even if you stopped the immediate threat.
Phase 1 — Preserve Before You Investigate
The most common forensic mistake is investigating a live system without preserving its state first. Every action you take on a compromised system — running commands, restarting services, applying patches — potentially overwrites evidence.
Immediate preservation steps:
# Capture current running processes before anything changes
ps aux > /tmp/forensic_processes_$(date +%Y%m%d_%H%M%S).txt
# Capture active network connections
ss -tulnp > /tmp/forensic_connections_$(date +%Y%m%d_%H%M%S).txt
netstat -an >> /tmp/forensic_connections_$(date +%Y%m%d_%H%M%S).txt
# Capture currently logged-in users
w > /tmp/forensic_users_$(date +%Y%m%d_%H%M%S).txt
last -n 50 >> /tmp/forensic_users_$(date +%Y%m%d_%H%M%S).txt
# Snapshot cron jobs — attackers frequently use these for persistence
crontab -l > /tmp/forensic_cron_$(date +%Y%m%d_%H%M%S).txt
cat /etc/crontab >> /tmp/forensic_cron_$(date +%Y%m%d_%H%M%S).txt
ls -la /etc/cron* >> /tmp/forensic_cron_$(date +%Y%m%d_%H%M%S).txt
Copy your log files immediately — before rotation or any service restart overwrites them.
# Archive all relevant logs
tar -czf /tmp/forensic_logs_$(date +%Y%m%d_%H%M%S).tar.gz \
/var/log/apache2/ \
/var/log/nginx/ \
/var/log/auth.log \
/var/log/syslog \
/var/log/fail2ban.log \
/var/www/html/wp-content/debug.log 2>/dev/null
Move these archives off the compromised system immediately — to a secure external location. Evidence on a compromised system is not evidence you can trust.
Every action you take on a compromised system — running commands, restarting services, applying patches — potentially overwrites evidence.
Phase 2 — Timeline Reconstruction
Every attack has a timeline. Reconstructing it precisely is the foundation of everything that follows.
Authentication Log Analysis
For a brute force attack against a WordPress installation, the authentication logs are your first source of truth.
# Extract all failed authentication attempts with timestamps
grep "authentication failure" /var/log/auth.log | \
awk '{print $1, $2, $3, $11, $13}' | \
sort | uniq -c | sort -rn | head -50
# For Apache — extract repeated POST requests to wp-login.php
grep "POST /wp-login.php" /var/log/apache2/access.log | \
awk '{print $1, $4, $7, $9}' | \
sort -k1,1 > /tmp/forensic_login_attempts.txt
# Count attempts per IP address
grep "POST /wp-login.php" /var/log/apache2/access.log | \
awk '{print $1}' | \
sort | uniq -c | sort -rn
In one incident I investigated, this query revealed over 400 authentication attempts from 23 distinct IP addresses across a 4-hour window. The distribution was deliberate — the attacker was rotating IPs to avoid simple rate limiting.
Building the Attack Timeline
Once you have raw log data, build a chronological sequence:
# Merge and sort logs by timestamp for a unified timeline
cat /var/log/apache2/access.log /var/log/apache2/error.log | \
sort -k4,4 > /tmp/forensic_unified_timeline.txt
# Find the first occurrence of the attacking IP
grep "ATTACKER_IP" /var/log/apache2/access.log | head -1
# Find the last occurrence
grep "ATTACKER_IP" /var/log/apache2/access.log | tail -1
Phase 3 — Attack Vector Analysis
Identifying Brute Force Patterns
Not all brute force attacks look the same. The log signature reveals the sophistication of the attacker.
Simple brute force — single IP, sequential requests, obvious pattern:
192.168.1.100 - - [15/Jun/2026:02:14:01] "POST /wp-login.php HTTP/1.1" 200 4521
192.168.1.100 - - [15/Jun/2026:02:14:02] "POST /wp-login.php HTTP/1.1" 200 4521
192.168.1.100 - - [15/Jun/2026:02:14:03] "POST /wp-login.php HTTP/1.1" 200 4521
Distributed brute force — multiple IPs, coordinated timing, harder to block by IP alone:
45.33.32.156 - - [15/Jun/2026:02:14:01] "POST /wp-login.php HTTP/1.1" 200 4521
198.20.69.74 - - [15/Jun/2026:02:14:01] "POST /wp-login.php HTTP/1.1" 200 4521
66.240.192.138 - - [15/Jun/2026:02:14:02] "POST /wp-login.php HTTP/1.1" 200 4521
Credential stuffing — using known username/password pairs from data breaches, lower volume, harder to detect by volume alone.
The distinction matters because each requires a different mitigation strategy.
Malicious File Upload Analysis
File upload attacks leave a different forensic signature. What you are looking for:
# Find recently modified files in web root — especially PHP files
find /var/www/html -name "*.php" -newer /var/www/html/wp-config.php \
-not -path "*/wp-content/uploads/cache/*" \
-ls 2>/dev/null
# Look for PHP files in upload directories — these should not exist
find /var/www/html/wp-content/uploads -name "*.php" -o \
-name "*.phtml" -o \
-name "*.php5" 2>/dev/null
# Check for obfuscated code — common in web shells
grep -r "base64_decode\|eval(\|gzinflate\|str_rot13" \
/var/www/html/wp-content/uploads/ 2>/dev/null
A web shell in the uploads directory is one of the most serious findings in a forensic investigation.
Phase 4 — Scope Determination
This is the question every incident response must answer: how far did they get?
Was Authentication Successful?
# Check for successful logins following failed attempts
# HTTP 302 after POST to wp-login.php indicates successful login
grep "POST /wp-login.php" /var/log/apache2/access.log | \
grep " 302 "
# Cross-reference with WordPress user activity
mysql -u root -p wordpress_db -e "
SELECT user_login, meta_value
FROM wp_users u
JOIN wp_usermeta m ON u.ID = m.user_id
WHERE meta_key = 'session_tokens'
ORDER BY u.user_registered DESC;"
File System Integrity Check
After any suspected compromise, verify the integrity of your core files:
# Generate checksums of all PHP files currently on disk
find /var/www/html -name "*.php" -exec md5sum {} \; > /tmp/current_checksums.txt
# Compare against known-good checksums
diff /tmp/known_good_checksums.txt /tmp/current_checksums.txt
# Check for recently created or modified files
find /var/www/html -newer /tmp/forensic_reference_time \
-type f -ls 2>/dev/null | sort -k8,9
Database Integrity
Attackers who gain access often target the database — for credentials, for data, or to inject malicious content.
-- Check for unauthorized admin accounts
SELECT ID, user_login, user_email, user_registered
FROM wp_users
WHERE ID IN (
SELECT user_id FROM wp_usermeta
WHERE meta_key = 'wp_capabilities'
AND meta_value LIKE '%administrator%'
)
ORDER BY user_registered DESC;
-- Look for injected content in post content
SELECT ID, post_title, post_modified
FROM wp_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%eval(%'
OR post_content LIKE '%base64%';
Phase 5 — Attribution and Pattern Analysis
Attribution in web forensics is not about identifying a person — it is about characterizing the threat actor's capabilities, tools, and intent. This informs your remediation and your future defenses.
IP Reputation and Geolocation
# Query IP reputation for each attacking IP
whois 45.33.32.156 | grep -E "OrgName|Country|NetName"
# Check if IPs belong to known Tor exit nodes, VPNs, or botnets
curl -s "https://ipinfo.io/45.33.32.156/json"
User Agent Analysis
# Extract user agents from attacking IPs
grep "ATTACKER_IP" /var/log/apache2/access.log | \
awk -F'"' '{print $6}' | \
sort | uniq -c | sort -rn
Phase 6 — Remediation with Forensic Discipline
Remediation without forensic discipline creates two problems: you may not fully remove the threat, and you destroy evidence you might need later.
The correct sequence:
- Isolate — if the compromise is confirmed, take the system offline or restrict network access before remediation
- Document — photograph, screenshot, and log everything before changing it
- Remove — eliminate malicious files, backdoors, unauthorized accounts
- Harden — implement the controls that would have prevented the attack
- Verify — confirm the threat is fully removed before bringing the system back online
- Monitor — increased logging and alerting for at least 30 days post-incident
# After remediation — verify no unauthorized processes are running
ps aux | grep -v "root\|www-data\|mysql\|known_service"
# Verify no unexpected outbound connections
ss -tulnp | grep ESTABLISHED
# Confirm no unauthorized cron jobs remain
crontab -l
cat /etc/cron* /var/spool/cron/crontabs/* 2>/dev/null
The Incident Report
Every forensic investigation should produce a written incident report — even if you are the only person who will read it. The discipline of writing forces clarity, and the document becomes invaluable if the attack recurs.
A minimal incident report contains:
- Executive summary — what happened, in two paragraphs
- Timeline — precise chronological sequence of events
- Attack vector — how the attacker operated
- Scope — what was accessed, modified, or exfiltrated
- Indicators of Compromise (IoCs) — IP addresses, file hashes, malicious file names, URL patterns
- Remediation actions taken — what was done and when
- Recommendations — what would have prevented this
Building a Forensic-Ready Infrastructure
The best time to prepare for forensic investigation is before an incident occurs:
- Centralized logging — ship logs to an external system in real time. An attacker who compromises your server can modify local logs; they cannot modify logs that have already been shipped.
- Immutable log storage — use append-only storage for security logs.
- Baseline documentation — maintain a known-good record of file checksums, running processes, and network connections.
- Alerting thresholds — set alerts for authentication failure rates, unusual file modifications, and new processes in web directories.
- Regular log review — not just when something goes wrong.
Final Thought
Web system forensics is not a reactive discipline. It is a mindset — one that assumes attacks will happen, builds systems that make them visible, and maintains the investigative discipline to reconstruct them fully when they do.
The goal is not to be unattackable. No system is. The goal is to know exactly what happened, respond precisely, and come out of every incident with a more defensible system than you had before.
That is what separates security engineering from security theater.