Volatile first
Core IR principle: collect memory, process state, and network connections BEFORE examining disk artifacts: volatile data is lost on reboot or when the attacker acts
SSH keys
SSH authorized_keys files: the most common Linux persistence mechanism; attackers add their public key to /root/.ssh/authorized_keys or any user's .ssh directory
auth.log
Primary authentication log on Debian/Ubuntu systems (/var/log/auth.log): records all SSH logins, sudo usage, and PAM authentication events
Web shells
PHP/Python files placed in web-accessible directories that provide remote command execution: the most common initial foothold on Linux web servers

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

A compromised Linux server investigation differs from Windows IR in several important ways: there is no central event log registry, persistence spans multiple mechanisms (cron, systemd, SSH keys, bash profiles, PAM modules), and attackers frequently modify timestamps and clean logs: making order of operations critical.

The golden rule: collect volatile evidence first, examine disk artifacts second. Rebooting the server before collecting network connections and process state loses evidence that would identify the C2 IP, the malware running in memory, and the attacker's active session.

Phase 1: Volatile Evidence Collection

Run these commands IMMEDIATELY: before any other action:

# 1. Capture system information snapshot:
date && hostname && uname -a > /tmp/snapshot.txt

# 2. Who is currently logged in:
w                        # Current sessions with IP and activity
last -100               # Last 100 logins (reads /var/log/wtmp)
lastb -100              # Failed logins (/var/log/btmp)
who -a                  # All logged-in users with TTY

# 3. Running processes:
ps auxf                 # All processes with full args, in tree format
# Look for: unusual process names, high CPU/memory, unexpected users
# pay attention to: www-data, apache, nginx spawning shells

# 4. Process network connections:
ss -tnpu                # All TCP/UDP connections with process info
# or:
netstat -tnpu
# Look for: ESTABLISHED connections to external IPs from unexpected processes
# Look for: LISTEN on unusual ports (backdoors typically listen on high ports)

# 5. Network statistics:
ss -s                   # Socket summary
ip route show           # Routing table (detect unauthorized routes added)
arp -a                  # ARP table (detect lateral movement neighbors)

# 6. Active network connections WITH process names and PIDs:
ss -tnpO
# Save to file:
ss -tnpO > /tmp/network_connections_$(date +%s).txt

# 7. List all open files and network sockets per process:
lsof -i -n -P          # All open network files (no DNS resolution)
lsof -u root           # All files opened by root

# 8. Capture process list with hashes (before any cleanup):
for pid in $(ls /proc/ | grep -E '^[0-9]+$'); do
    exe=$(readlink -f /proc/$pid/exe 2>/dev/null)
    cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')
    if [ -n "$exe" ]; then
        hash=$(sha256sum "$exe" 2>/dev/null | cut -d' ' -f1)
        echo "PID:$pid EXE:$exe HASH:$hash CMD:$cmd"
    fi
done > /tmp/process_hashes.txt

Phase 2: Persistence Mechanism Examination

Examine all persistence locations: attackers commonly use multiple:

# SSH authorized keys (most common persistence):
for user in $(cut -d: -f1 /etc/passwd); do
    home=$(getent passwd $user | cut -d: -f6)
    keyfile="$home/.ssh/authorized_keys"
    if [ -f "$keyfile" ]; then
        echo "=== $user: $keyfile ==="
        ls -la "$keyfile"
        cat "$keyfile"
    fi
done
# Also check /root/.ssh/authorized_keys directly:
cat /root/.ssh/authorized_keys 2>/dev/null

# Cron jobs (all users and system):
crontab -l -u root 2>/dev/null
for user in $(cut -d: -f1 /etc/passwd); do
    crontab -l -u $user 2>/dev/null && echo "--- for $user ---"
done
cat /etc/crontab
ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/
cat /etc/cron.d/* 2>/dev/null

# Systemd units (persistent service-based backdoors):
find /etc/systemd /lib/systemd /usr/lib/systemd -name "*.service" \
  -newer /usr/bin/systemd -ls 2>/dev/null
# Units newer than systemd binary = recently added (suspicious)
systemctl list-units --type=service --state=running

# Bash profile and rc files (executed on login/shell start):
for f in /etc/profile /etc/profile.d/*.sh /root/.bashrc /root/.bash_profile \
         /root/.profile; do
    echo "=== $f ==="
    cat "$f" 2>/dev/null
done

# PAM modules (rootkit indicator: modified PAM auth):
cat /etc/pam.d/sshd /etc/pam.d/common-auth
ls -la /lib/x86_64-linux-gnu/security/  # Check for unexpected PAM modules

Find recently modified files (most critical):

# Files modified in the last 7 days (adjust -mtime as needed):
find / -mtime -7 -type f -not -path '/proc/*' -not -path '/sys/*' \
  -not -path '/dev/*' -ls 2>/dev/null | sort -k8,9

# Specifically: recently modified SUID binaries (potential rootkit install):
find / -mtime -7 -perm /4000 -type f -ls 2>/dev/null

# Recently created/modified files in web directories (web shells):
find /var/www /srv/www /usr/share/nginx /opt/*/web -mtime -30 \
  -name '*.php' -o -name '*.py' -o -name '*.pl' -o -name '*.jsp' \
  2>/dev/null | xargs ls -la 2>/dev/null
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Phase 3: Log Analysis

Authentication log analysis:

# Ubuntu/Debian:
grep 'sshd' /var/log/auth.log | tail -200
# Look for: Accepted password/publickey (successful logins), Failed password (brute force)
# Look for: sudo commands executed by attacker

# Find all successful SSH logins:
grep 'Accepted' /var/log/auth.log | \
  awk '{print $1, $2, $3, $9, $11}' | \
  sort -u
# Output: Month Day Time username source_IP

# Find sudo command execution:
grep 'sudo' /var/log/auth.log | grep -v 'pam_unix'
# Shows: which user ran which command as sudo

# RHEL/CentOS:
grep 'sshd\|sudo' /var/log/secure | tail -200

Web server log analysis (web shell detection):

# Apache access log: look for POST requests to PHP files in unusual locations:
grep 'POST' /var/log/apache2/access.log | \
  awk '{print $1, $6, $7, $9}' | \
  grep -E '\.php|\.(cgi|asp|aspx|sh)' | \
  tail -100

# Nginx access log: look for command execution patterns in URLs:
grep -E '(cmd=|exec=|system\(|passthru|shell_exec)' \
  /var/log/nginx/access.log | tail -50

# Find the first time a suspicious PHP file was requested:
awk '{print $7}' /var/log/nginx/access.log | \
  grep -o 'wp-content.*\.php' | sort | uniq -c | sort -rn | head -20
# High-count PHP files in unexpected locations = web shell candidates

# Look for common web shell user agents:
grep -E '(python|curl|wget|Go-http|libwww-perl)' /var/log/nginx/access.log | \
  grep 'POST' | tail -50
# Web shell interactions often use scripted user agents

Detect log tampering:

# Check if auth.log has gaps (attacker may have deleted entries):
ls -lah /var/log/auth.log*  # Check rotation and size
stat /var/log/auth.log       # Mtime = last write time

# auditd can detect log modification if properly configured:
ausearch -k log_modification 2>/dev/null

# Check /var/log/lastlog for login history:
lastlog | head -30  # Shows last login per account

# Check utmp/wtmp for session history:
last -F | head -50  # -F shows full timestamps

Phase 4: Web Shell and Rootkit Detection

Find web shells with content scanning:

# Look for PHP code patterns common in web shells:
grep -r --include='*.php' -l 'eval\|base64_decode\|system\|passthru\|shell_exec\|exec' \
  /var/www/ 2>/dev/null

# More specific: eval(base64_decode(...)) is the classic web shell pattern:
grep -r --include='*.php' 'eval(base64_decode' /var/www/ 2>/dev/null

# Find PHP files with suspicious permissions or in unexpected locations:
find /var/www /tmp /var/tmp -name '*.php' -ls 2>/dev/null
find /var/www -name '*.php' -perm /o+w -ls 2>/dev/null
# World-writable PHP files = extremely suspicious

# Scan with Linux Malware Detect (LMD) or ClamAV:
clamscan -r --infected /var/www/ 2>/dev/null

# Check for hidden files in web directories:
find /var/www -name '.*' -not -name '.htaccess' -ls 2>/dev/null
# Hidden files (starting with .) in web root = suspicious

Rootkit detection:

# Install and run rkhunter:
apt install rkhunter -y
rkhunter --update && rkhunter --check --sk

# Install and run chkrootkit:
apt install chkrootkit -y
chkrootkit

# Check for LD_PRELOAD rootkits (intercept shared library calls):
cat /etc/ld.so.preload  # Should normally be empty
env | grep LD_PRELOAD   # Should normally be empty

# Check for hidden processes using /proc vs ps:
ls /proc/ | grep -E '^[0-9]+$' > /tmp/proc_pids.txt
ps aux | awk '{print $2}' | tail -n +2 | sort -n > /tmp/ps_pids.txt
diff /tmp/proc_pids.txt /tmp/ps_pids.txt
# PIDs in /proc but not in ps = hidden by rootkit

The bottom line

Linux server IR follows this order: (1) collect volatile evidence first: w, ps auxf, ss -tnpu, lsof -i: before any other action; (2) examine persistence: SSH authorized_keys, cron, systemd units, bash profiles; (3) analyze logs: auth.log for SSH/sudo activity, web server logs for POST requests to PHP files; (4) find web shells: grep -r 'eval(base64_decode' /var/www/; (5) detect rootkits: rkhunter, chkrootkit, compare /proc vs ps for hidden processes. Hash every suspicious file and check VirusTotal before deleting. Always collect evidence before remediation to preserve the ability to understand the full attack chain.

Frequently asked questions

What should you do first when you suspect a Linux server is compromised?

Collect volatile evidence before doing anything else: run `w` (current users), `ps auxf` (all processes with arguments), `ss -tnpu` (all network connections with process names), and `lsof -i -n -P` (all open network files). This data is lost on reboot. Then capture the process executable hashes to a file for VirusTotal verification. Only after volatile collection should you examine disk artifacts like cron jobs, authorized_keys, and log files.

How do you find a web shell on a compromised Linux server?

Run `grep -r --include='*.php' -l 'eval\|base64_decode\|system\|shell_exec' /var/www/` to find PHP files containing web shell patterns. Specifically `eval(base64_decode(...))` is the most common web shell signature. Also check: `find /var/www -mtime -30 -name '*.php' -ls` for recently created PHP files, `find /var/www -name '.*' -ls` for hidden files in web directories, and examine web server access logs for POST requests to PHP files from scripted user agents (curl, python, wget).

How do you investigate initial access on a compromised Linux server?

Linux initial access investigation sequence: (1) Check auth.log or /var/log/secure for SSH brute force and successful logins from external IPs before the compromise window. (2) Review web server access logs (Apache: /var/log/apache2/access.log, Nginx: /var/log/nginx/access.log) for exploit signatures: large POST bodies to PHP files, path traversal sequences, SQL injection patterns. (3) Check /var/log/syslog for sudo executions and cron job additions. (4) Run 'last -F' to list all user logins with timestamps and source IPs. (5) Check bash_history for all user accounts: 'cat /home/*/.bash_history /root/.bash_history'. Note: sophisticated attackers clear bash_history — absence of history is itself suspicious for accounts that were logged into.

How do you identify and remove Linux rootkits after a server compromise?

Linux rootkit detection: run rkhunter ('rkhunter --check') and chkrootkit to scan for known rootkit signatures and system binary modifications. Both compare checksums of system binaries against expected values. For kernel-level rootkits: check for unexpected kernel modules with 'lsmod' and 'modinfo' on suspicious entries; compare running modules against a baseline. If a rootkit is confirmed, do NOT trust any tools on the compromised system — the attacker may have replaced ls, find, netstat, and ps with versions that hide the rootkit. Use a bootable live CD or trusted binaries from a clean system. For compromised servers, the safest remediation is wiping and rebuilding from a known-good image rather than attempting in-place cleaning.

What Linux persistence mechanisms should I check after a compromise?

Check all Linux persistence locations: crontab for all users ('crontab -l -u <user>' for each user, plus /etc/cron.d/, /etc/cron.daily/, /etc/cron.hourly/); systemd services (ls /etc/systemd/system/*.service and systemctl list-units --type=service -- compare against baseline); /etc/rc.local and /etc/init.d/ for legacy startup scripts; /etc/profile, /etc/bashrc, and ~/.bashrc/.bash_profile for shell initialization persistence; authorized_keys files for all user accounts (attackers add their SSH key for re-entry); and LD_PRELOAD in /etc/ld.so.preload (a library listed here loads before all other libraries -- used for shared library injection persistence).

How do you preserve forensic evidence from a compromised Linux server for legal proceedings or external forensic analysis?

Forensic preservation requires maintaining chain of custody from collection through storage. Before any remediation: acquire a full memory image using avml ('avml /mnt/external/memory.lime') or the LiME kernel module, run from read-only media or a trusted remote tool to avoid contaminating the system. Create bit-for-bit disk images using dc3dd with hash verification: 'dc3dd if=/dev/sda of=/mnt/external/sda.img hash=sha256 log=/mnt/external/sda.log'. Calculate SHA256 hashes of every collected artifact and record them in a signed chain-of-custody document at the time of collection. Store evidence on write-protected external media or an external system with tamper detection. For cloud instances: take a VM snapshot before any remediation -- the snapshot captures disk state and is a valid forensic artifact for most legal proceedings. Critical constraint: do not run rkhunter, clamscan, or any tool that writes to the system disk before imaging, as modifications after the incident affect evidence integrity and may undermine legal proceedings.

Sources & references

  1. SANS: Linux IR Playbook
  2. Google Security: Linux Incident Response Guide
  3. NIST SP 800-61: Computer Security Incident Handling Guide

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.