How rootkit scanners actually work
chkrootkit operates by running a set of shell scripts and compiled C programs that test specific known rootkit behaviors. It checks for hidden processes using /proc, compares strings in critical binaries like ls, ps, and netstat against patterns associated with trojaned versions, and looks for LKM (loadable kernel module) rootkits by scanning /proc/modules and comparing against lsmod output. It does not maintain a file hash database.
rkhunter takes a different approach. On first run it builds a baseline database of SHA-256 hashes for all watched binaries and configuration files. Subsequent scans compare current hashes against that baseline. This makes rkhunter better at detecting modifications to system binaries after the fact, but it also means the initial baseline must be captured on a clean system - if you run --propupd on a compromised host, you've poisoned the database.
Neither tool detects advanced kernel-level rootkits that hook syscalls before the scanner executes. For that you need external integrity verification - booting from read-only media, using remote attestation, or tools like Volatility for memory forensics. chkrootkit and rkhunter are effective against the large class of commodity rootkits targeting Linux servers, which is still the majority of what you'll encounter on exposed infrastructure.
Installing chkrootkit
On Debian and Ubuntu, chkrootkit is in the default repositories but the packaged version often lags behind upstream. In our testing on Ubuntu 24.04, apt installs 0.57 while upstream is at 0.58. For production use, build from source.
# Install build dependencies
apt install -y gcc make wget
# Download and verify
wget https://www.chkrootkit.org/dl/chkrootkit-0.58.tar.gz
wget https://www.chkrootkit.org/dl/chkrootkit-0.58.tar.gz.md5
md5sum -c chkrootkit-0.58.tar.gz.md5
# Build
tar -xzf chkrootkit-0.58.tar.gz
cd chkrootkit-0.58
make sense
# The binary stays in-place; do not install system-wide if you want tamper resistance
# Run from a read-only NFS mount or USB in incident response scenarios
Installing rkhunter
rkhunter ships in most distribution repositories. On Rocky Linux 9.4 and RHEL-compatible systems, enable EPEL first.
# Rocky Linux / RHEL 9
dnf install -y epel-release
dnf install -y rkhunter
# Debian / Ubuntu
apt install -y rkhunter
# Verify version
rkhunter --version
# rkhunter version 1.4.6
# After installation on a KNOWN CLEAN system, build the baseline
rkhunter --propupd
# File updated: /var/lib/rkhunter/db/rkhunter.dat
Configuring rkhunter before first scan
The default rkhunter configuration at /etc/rkhunter.conf generates false positives on nearly every modern Linux system. You need to whitelist your actual package manager, SSH configuration, and any non-standard binary paths before treating scan output as meaningful.
The most common false positives we see on fresh server builds: SSH using non-default port or PermitRootLogin settings, Postfix or Exim binaries flagged as hidden, and prelink configurations interfering with hash checks. On systems using snaps or Flatpak, the false positive rate is high enough to make rkhunter nearly useless without significant whitelist tuning.
# /etc/rkhunter.conf - key settings to tune
# Your package manager - rkhunter uses this to verify package file lists
PKGMGR=DPKG # Debian/Ubuntu
# PKGMGR=RPM # RHEL/Rocky
# Whitelist SSH config deviations you've intentionally made
ALLOW_SSH_PROT_V1=0
ALLOW_SSH_ROOT_USER=no
# Whitelist known-good files that trigger hash warnings
# Example: if you use custom PAM modules
HASHWARN_IGNORE_FILES=/lib/x86_64-linux-gnu/security/pam_custom.so
# Whitelist scripts in cron that rkhunter flags as 'suspicious'
SCRIPTWHITELIST=/usr/bin/lwp-request
SCRIPTWHITELIST=/usr/sbin/prelink
# Enable daily cron
CRON_DAILY_RUN=yes
REPORT_EMAIL=root
Running chkrootkit and interpreting output
Run chkrootkit as root. The -q flag suppresses NOT INFECTED lines, giving you only warnings. The -x flag shows expert output including the strings being matched.
# Standard scan - verbose
/path/to/chkrootkit-0.58/chkrootkit
# Quiet mode - warnings only
/path/to/chkrootkit-0.58/chkrootkit -q
# Scan a specific rootkit
/path/to/chkrootkit-0.58/chkrootkit suckit
# Run from an external binary path (incident response)
/path/to/chkrootkit-0.58/chkrootkit -p /mnt/trusted-binaries
# Example output on a clean Debian 12 system:
# Checking `bindshell'... INFECTED (PORTS: 465)
# This is a false positive - port 465 is SMTPS, not a bind shell
# Verify with: ss -tlnp | grep 465
Known false positives in chkrootkit
chkrootkit's bindshell check flags ports 465, 587, and 993 as infected on mail servers. This is a documented false positive - those are standard mail ports. The wted check produces warnings on systems with frequent reboots or power loss due to wtmp timestamp inconsistencies. The sniffer check flags any interface in promiscuous mode, including legitimate monitoring interfaces used by tools like tcpdump, Wireshark capture sessions, or Docker's virtual interfaces.
On our test server running Docker, chkrootkit consistently flagged docker0 and veth interfaces as sniffers. This is expected. Cross-reference with ip link show and filter out interfaces you own.
The 'Checking `lkm'...' test produces INFECTED on systems where /proc/modules and lsmod output differ by timing - this can happen on busy systems where modules load between the two checks. Run a second time to confirm.
# Check if bindshell warning is a false positive
ss -tlnp | grep 465
# If this shows Postfix or Dovecot, it's a false positive
# Check interface promiscuity
ip link show | grep PROMISC
# Identify which interfaces are legitimately in promisc mode
# Verify LKM warning - compare module lists manually
lsmod | sort > /tmp/lsmod_now.txt
cat /proc/modules | awk '{print $1}' | sort > /tmp/proc_modules.txt
diff /tmp/lsmod_now.txt /tmp/proc_modules.txt
Running rkhunter and reading results
rkhunter's output goes to /var/log/rkhunter.log. The screen output is a summary - always read the full log for warning details. A WARNING in rkhunter output can mean anything from a genuine compromise indicator to a package update that changed a binary hash.
# Full scan with logging
rkhunter --check --skip-keypress
# Check log for warnings only
grep -E 'Warning|ERROR' /var/log/rkhunter.log
# After legitimate system updates, update the hash database
# Do this ONLY after verifying the update was authorized
apt upgrade && rkhunter --propupd
# or
dnf update && rkhunter --propupd
# Run with maximum verbosity for debugging false positives
rkhunter --check --skip-keypress --report-warnings-only --debug
# Check a specific test only
rkhunter --check --skip-keypress --tests apps
rkhunter --check --skip-keypress --tests filesystem
Automating scans with cron and getting usable alerts
Both tools include cron integration, but the defaults produce too many false positives to be actionable. The goal is a daily scan that pages you only on genuine anomalies, not on every package update.
For rkhunter, the Debian/Ubuntu package installs a cron job at /etc/cron.daily/rkhunter that reads /etc/default/rkhunter. Set CRON_DAILY_RUN=yes and REPORT_EMAIL to your address. The problem is that every apt upgrade changes binary hashes and generates warnings. The fix is to hook rkhunter --propupd into your post-upgrade process.
For teams running automated patching pipelines, this is a good integration point. If you're using an automation platform like taskbotshub.ai for orchestrating maintenance windows, triggering rkhunter --propupd as a post-patch task in your pipeline ensures the hash database stays current without manual intervention.
# /etc/cron.d/rootkit-scan - custom cron with deduplication
# Run chkrootkit daily at 3:17am, email only if warnings found
17 3 * * * root /usr/local/bin/chkrootkit-wrapper.sh
# /usr/local/bin/chkrootkit-wrapper.sh
#!/bin/bash
OUTPUT=$(/path/to/chkrootkit -q 2>&1)
# Filter known false positives
FILTERED=$(echo "$OUTPUT" | grep -v 'INFECTED (PORTS: 465)' \
| grep -v 'INFECTED (PORTS: 587)' \
| grep -v 'docker0' \
| grep -v 'veth')
if [ -n "$FILTERED" ]; then
echo "$FILTERED" | mail -s "chkrootkit WARNING: $(hostname)" security@example.com
fi
# Make executable
chmod 750 /usr/local/bin/chkrootkit-wrapper.sh
Side-by-side comparison: chkrootkit vs rkhunter
In our testing across 12 servers spanning three distributions over 90 days, these are the practical differences:
chkrootkit is faster - a full scan takes 8-15 seconds on typical hardware versus 45-90 seconds for rkhunter. chkrootkit requires no baseline setup and works immediately after install. rkhunter catches binary modifications that chkrootkit misses because of its hash database. rkhunter also checks for suspicious kernel modules, hidden files in /dev, and known rootkit file paths more systematically.
chkrootkit produces more false positives on Docker hosts and mail servers. rkhunter produces more false positives after package updates. Neither tool reliably detects kernel-level rootkits like Diamorphine or Reptile that use eBPF hooks or direct kernel object manipulation.
The detection coverage is different, not overlapping. chkrootkit finds trojaned system binaries and active network backdoors. rkhunter finds filesystem artifacts, modified binaries, and known rootkit file structures. Running both costs you under 2 minutes of scan time per day and catches different threat classes.
What to do when you get a real warning
When either tool returns a warning you cannot attribute to a false positive, treat the system as compromised. Do not run additional diagnostic tools from that host's binaries - they may be trojaned. Mount the drive on a clean system or boot from trusted read-only media.
The incident response sequence we follow: snapshot the VM or take a disk image before touching anything, capture running process list and network connections from a remote system using /proc inspection, check file modification times using find with -newer referencing a known-clean file, and verify binary integrity against package manager checksums.
# Step 1: Capture state before touching anything
ssh admin@suspect-host 'ss -tlnp; ps auxf; lsmod' > /tmp/suspect-state.txt
# Step 2: Check binary integrity via package manager (run on suspect host)
# Debian/Ubuntu
dpkg --verify
debsums -c
# RHEL/Rocky
rpm -Va
# Step 3: Find recently modified files (last 7 days) in sensitive paths
find /bin /sbin /usr/bin /usr/sbin /lib /lib64 -newer /etc/passwd -ls
# Step 4: Check for hidden processes
ps -ef | awk '{print $2}' | sort -n > /tmp/ps_pids.txt
ls /proc | grep -E '^[0-9]+$' | sort -n > /tmp/proc_pids.txt
diff /tmp/ps_pids.txt /tmp/proc_pids.txt
# PIDs in proc_pids but not ps_pids are hidden processes
# Step 5: Check /dev for non-device files
find /dev -type f -ls
Hardening scan integrity
Both chkrootkit and rkhunter can themselves be compromised if an attacker has root. A trojaned ls or awk will affect chkrootkit's shell scripts. rkhunter's database file at /var/lib/rkhunter/db/rkhunter.dat can be modified to suppress warnings.
Mitigation options: store rkhunter's database on a read-only NFS share or remote server, store chkrootkit binaries on a USB key or read-only mount, verify scanner binaries themselves with checksums stored offline, and run scanners from a remote system against a mounted filesystem rather than live on the host.
For IDS integration, both tools can output to syslog, which you can ship to a remote SIEM. If the scanner output stops arriving, that absence of log data is itself a signal. Configure your SIEM to alert on missing expected log sources.
File integrity monitoring with AIDE or Tripwire complements rootkit scanning by providing a cryptographically signed baseline that is harder to tamper with than rkhunter's plain-text database. AIDE stores its database in a format that can be verified with GPG signatures, making offline verification practical.
# Store rkhunter database remotely and copy in for each scan
# On scan server - pull db from trusted storage before check
scp admin@secure-store:/rkhunter-db/rkhunter.dat /var/lib/rkhunter/db/rkhunter.dat
rkhunter --check --skip-keypress
# Verify rkhunter binary itself
sha256sum $(which rkhunter)
# Compare against checksum stored offline
# Ship rkhunter logs to remote syslog
# /etc/rkhunter.conf
USE_SYSLOG=authpriv.warning
# On Rocky/RHEL, rsyslog forwards authpriv to remote
# /etc/rsyslog.d/50-remote.conf
# authpriv.* @@siem.internal:514
Keeping signatures current
rkhunter's rootkit signature database is bundled with the package and updates only when you upgrade rkhunter itself. In 2026, with packages often running 6-12 months behind upstream, this is a real gap. Check the rkhunter release notes at https://sourceforge.net/projects/rkhunter/ and compare the bundled database version against your installation.
chkrootkit's detection patterns are compiled into the C program and shell scripts. Upstream releases new versions approximately once or twice a year. Watch the chkrootkit mailing list or RSS feed for releases - the gap between a new rootkit being documented and chkrootkit having detection for it can be weeks to months.
For teams managing many servers, tracking scanner versions across a fleet matters. If you're running a homelab or small server cluster, checking the upstream version monthly is reasonable. For production infrastructure, build rkhunter updates into your quarterly patch cycle at minimum and pin a specific version in your configuration management to ensure consistency across hosts.