# Show all established outbound TCP connections with the owning process
ss -tnpo state established # Group by remote IP to spot fan-out patterns
ss -tn state established | awk 'NR>1 {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
# Show all established outbound TCP connections with the owning process
ss -tnpo state established # Group by remote IP to spot fan-out patterns
ss -tn state established | awk 'NR>1 {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
# Show all established outbound TCP connections with the owning process
ss -tnpo state established # Group by remote IP to spot fan-out patterns
ss -tn state established | awk 'NR>1 {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
pstree -panu
pstree -panu
pstree -panu
# World-writable temp dirs are the #1 dropzone
ls -lat /tmp /var/tmp /dev/shm 2>/dev/null | head -30 # Recently modified files in system bin dirs
find /usr/bin /usr/sbin /usr/local/bin -mtime -30 -ls 2>/dev/null # Cron entries for every user (not just root)
for u in $(cut -d: -f1 /etc/passwd); do echo "--- $u ---" crontab -u "$u" -l 2>/dev/null
done # Systemd timers and services added recently
-weight: 500;">systemctl list-timers --all
find /etc/systemd/system /lib/systemd/system -mtime -60 -ls
# World-writable temp dirs are the #1 dropzone
ls -lat /tmp /var/tmp /dev/shm 2>/dev/null | head -30 # Recently modified files in system bin dirs
find /usr/bin /usr/sbin /usr/local/bin -mtime -30 -ls 2>/dev/null # Cron entries for every user (not just root)
for u in $(cut -d: -f1 /etc/passwd); do echo "--- $u ---" crontab -u "$u" -l 2>/dev/null
done # Systemd timers and services added recently
-weight: 500;">systemctl list-timers --all
find /etc/systemd/system /lib/systemd/system -mtime -60 -ls
# World-writable temp dirs are the #1 dropzone
ls -lat /tmp /var/tmp /dev/shm 2>/dev/null | head -30 # Recently modified files in system bin dirs
find /usr/bin /usr/sbin /usr/local/bin -mtime -30 -ls 2>/dev/null # Cron entries for every user (not just root)
for u in $(cut -d: -f1 /etc/passwd); do echo "--- $u ---" crontab -u "$u" -l 2>/dev/null
done # Systemd timers and services added recently
-weight: 500;">systemctl list-timers --all
find /etc/systemd/system /lib/systemd/system -mtime -60 -ls
sha256sum /tmp/.suspicious_binary
# Then upload the hash (not the file) to virustotal.com
sha256sum /tmp/.suspicious_binary
# Then upload the hash (not the file) to virustotal.com
sha256sum /tmp/.suspicious_binary
# Then upload the hash (not the file) to virustotal.com
# Watch execve syscalls — every program execution
auditctl -a always,exit -F arch=b64 -S execve -k exec_trace # Watch writes to common malware dropzones
auditctl -w /tmp -p wa -k tmp_writes
auditctl -w /dev/shm -p wa -k shm_writes # Watch SSH key file changes (very common persistence trick)
auditctl -w /root/.ssh/authorized_keys -p wa -k ssh_keys
# Watch execve syscalls — every program execution
auditctl -a always,exit -F arch=b64 -S execve -k exec_trace # Watch writes to common malware dropzones
auditctl -w /tmp -p wa -k tmp_writes
auditctl -w /dev/shm -p wa -k shm_writes # Watch SSH key file changes (very common persistence trick)
auditctl -w /root/.ssh/authorized_keys -p wa -k ssh_keys
# Watch execve syscalls — every program execution
auditctl -a always,exit -F arch=b64 -S execve -k exec_trace # Watch writes to common malware dropzones
auditctl -w /tmp -p wa -k tmp_writes
auditctl -w /dev/shm -p wa -k shm_writes # Watch SSH key file changes (very common persistence trick)
auditctl -w /root/.ssh/authorized_keys -p wa -k ssh_keys
# Look for shell execution chains from web server users
ausearch -k exec_trace ---weight: 500;">start recent | grep -E 'uid=(33|www-data|nginx|apache)'
# Look for shell execution chains from web server users
ausearch -k exec_trace ---weight: 500;">start recent | grep -E 'uid=(33|www-data|nginx|apache)'
# Look for shell execution chains from web server users
ausearch -k exec_trace ---weight: 500;">start recent | grep -E 'uid=(33|www-data|nginx|apache)'
# Drop all outbound traffic except SSH from your management IP
nft add table inet filter
nft add chain inet filter output { type filter hook output priority 0 \; policy drop \; }
nft add rule inet filter output ct state established,related accept
nft add rule inet filter output oifname lo accept
# Add your specific allowlist rules here
# Drop all outbound traffic except SSH from your management IP
nft add table inet filter
nft add chain inet filter output { type filter hook output priority 0 \; policy drop \; }
nft add rule inet filter output ct state established,related accept
nft add rule inet filter output oifname lo accept
# Add your specific allowlist rules here
# Drop all outbound traffic except SSH from your management IP
nft add table inet filter
nft add chain inet filter output { type filter hook output priority 0 \; policy drop \; }
nft add rule inet filter output ct state established,related accept
nft add rule inet filter output oifname lo accept
# Add your specific allowlist rules here - Slightly elevated baseline CPU (5-15% from nothing)
- Unexplained outbound traffic to weird ports
- New cron entries you didn't write
- SSH login attempts spiking from your own server's outbound logs (it's scanning for the next victim)
- Mysterious processes with names like kdevtmpfsi, xmrig, or randomly-generated 6-char names - Snapshot first. If your provider supports it, take a disk snapshot before you change anything. You may need it for forensics or to satisfy an abuse report.
- Cut network egress before killing processes. A common mistake is killing the miner first, which triggers a watchdog reinstall. Block outbound first: - Kill the processes and -weight: 500;">remove persistence. Cron, systemd units, ~/.bashrc lines, /etc/ld.so.preload, modified SSH authorized_keys. Check all of them.
- Rotate every credential that touched the box. SSH keys, API tokens in env files, database creds, cloud provider credentials. Assume they're all in someone's wallet now.
- Reinstall, don't clean. I know. It's annoying. But a rootkit you didn't find will outlive your cleanup. If the box held anything sensitive, the right move is destroy and rebuild from a known-good config. - Unattended upgrades disabled. Turn them on. unattended-upgrades on Debian/Ubuntu, -weight: 500;">dnf-automatic on RHEL-likes. Yes it's a small risk. Running months-old kernels is a much bigger one.
- SSH password auth enabled. Set PasswordAuthentication no and use keys. Add fail2ban for the noise.
- No egress filtering. Default-deny outbound is annoying to set up but it's the single thing that would have prevented every botnet enrollment I've seen. Even a basic rule blocking outbound to common scan ports (22, 23, 445, 3389) from anywhere but a specific allowlist would catch most of them.
- Forgotten services. That staging box. That old CMS. That Redis you exposed for "five minutes" to test something. Run nmap against your own external IPs once a quarter. You will be surprised.