
linux security checks
Crypto-miner detection, removal, and hardening toolkit for Linux servers.
Built from real-world incident response — detects miners that hide from
ps,top,htop, andbtopusing rootkit techniques.
git clone https://gitlab.com/abdom.seada/security-checks.git
cd security-checks
sudo bash setup.sh
🔀 Branch:
master— This toolkit lives on themasterbranch. Other security scripts may be added on separate branches in the future.
⚠️ Run
setup.shonce right after cloning — skipping this is the #1 cause of errors.
sudo bash setup.sh
setup.sh handles everything automatically:
Expected output when setup succeeds:
✅ Setup complete — all checks passed!
Next steps:
sudo ./miner-hunter scan # Safe read-only scan
sudo ./miner-hunter full # Scan → Kill → Harden
💡 Why is this needed? Linux won't execute a file unless it has the
+xflag. Git and SCP transfers strip this.setup.shfixes all files in one shot — including thelib/modules the main script depends on.
sudo ./miner-hunter scan # ✅ Safe — read-only, zero changes
sudo ./miner-hunter full # ⚠️ Full pipeline: Scan → Kill → Harden
sudo ./miner-hunter scan --dry-run # 👁️ Preview mode — shows what would happen
| Option | Description |
|---|---|
-d, --dry-run |
Real-world situations and exactly what to run in each one.
top shows nothing"This is the classic rootkit symptom. The miner is hiding from userspace tools but cannot hide from hardware performance counters.
# Step 1: Run a safe scan first — confirm what's there before touching anything
sudo ./miner-hunter scan
What you'll see if a miner is present:
🚨 [CRITICAL] CPU anomaly: 97% user CPU but top shows max 2% per process
🚨 [CRITICAL] perf detected 4 hidden threads consuming ~94% total CPU
🚨 [CRITICAL] Active connection to 185.x.x.x:9200 (known mining port)
🚨 [CRITICAL] Fake kernel thread PID=3421 NAME=[kworker/0:1] EXE=/tmp/.x/miner
# Step 2: Kill the miner and block its pool
sudo ./miner-hunter kill
# Step 3: Harden the server so it can't come back
sudo ./miner-hunter harden
You noticed something suspicious — unusual outbound traffic, a cron job you didn't create, a process with a weird name — but you're not certain.
# Run a full scan — completely safe, read-only, zero changes
sudo ./miner-hunter scan
# Then read the structured report
sudo ./miner-hunter report
The report at /root/miner_evidence_*/report.txt categorizes every finding by severity:
[CRITICAL] entries → proceed to kill immediately[WARNING] entries → review manually before actingThe miner has a persistence mechanism — a cron job, systemd service, PM2 entry, or shell profile backdoor that respawns it after you kill it.
sudo ./miner-hunter scan
Look for these in the output:
⚠️ [WARN] Suspicious cron entry: * * * * * /tmp/.x/update
🚨 [CRITICAL] Malicious systemd service: /etc/systemd/system/update-check.service
🚨 [CRITICAL] PM2 process 'app-worker' has 8432 restarts — likely miner respawn loop
🚨 [CRITICAL] Shell profile backdoor detected in /root/.bashrc
# kill removes ALL persistence artifacts — not just the running process
sudo ./miner-hunter kill
# Then harden to install the watchdog so you're alerted if anything respawns
sudo ./miner-hunter harden
💡 After
kill, the watchdog cron runs every 5 minutes and logs to/var/log/miner_hunter/watchdog_alerts.log— you'll know immediately if something comes back.
Proactive hardening before deploying — no miner, no incident, just locking things down.
# Run harden standalone — no scan or kill needed
sudo ./miner-hunter harden
This will:
sshd jail/usr/bin integrity baseline (MD5 checksums — so you can detect tampered binaries later)After kill, the verify step reports the miner may still be running:
⚠️ MINER MAY HAVE RESPAWNED
CPU: 89% | Mining conns: 1
Firewall blocks are in place — miner can't reach pool
Consider a REBOOT or OS REINSTALL
# 1. Firewall blocks are already in place — miner CANNOT reach its pool
# Confirm blocks are active:
iptables -L OUTPUT -n | grep DROP
# 2. Run a second scan to see what survived
sudo ./miner-hunter scan
# 3. Check for a kernel module rootkit hiding the process
lsmod | grep -iE 'diamorphine|reptile|kovid|rootkit'
# 4. Non-zero taint = out-of-tree kernel modules loaded (rootkit indicator)
cat /proc/sys/kernel/tainted
If the kernel taint value is non-zero or a known rootkit module appears — the miner has kernel-level control. The safest path at this point is a full OS reinstall from a known-clean snapshot.
After harden, the watchdog cron is already installed. Here's how to work with it:
# Watch the alert log in real time
tail -f /var/log/miner_hunter/watchdog_alerts.log
# Confirm the watchdog cron job is registered
cat /etc/cron.d/miner-watchdog
# Check for /usr/bin binary changes since your baseline was taken
md5sum --check /var/lib/miner_hunter/usrbin_baseline.md5 --quiet
Any output from the last command means a system binary was modified after your baseline — investigate immediately.
| Technique | What it catches |
|---|---|
perf hardware PMC profiling | Hidden CPU consumers — rootkits cannot fake hardware counters |
/proc delta sampling |
| Technique | What it catches |
|---|
When you run sudo ./miner-hunter kill, this is the exact sequence:
DROP rules applied before killing, so the miner can't reconnect even if it respawnsSIGKILLperf and checks /proc/net/tcp to confirm CPU dropped and connections are gonesecurity-checks/ ← repo root (master branch)
├── miner-hunter # Entry point — this is what you run
├── setup.sh # ⚙️ First-time setup — run once after cloning
├── lib/
│ ├── common.sh # Shared utilities: logging, colors, helpers
│ ├── detect_hidden.sh # Hidden process & rootkit detection
│ ├── detect_cpu.sh # CPU profiling via perf & /proc
│ ├── detect_network.sh # Mining pool connection detection
│ ├── detect_persistence.sh # Persistence mechanism detection
│ ├── kill_miner.sh # Process killing & artifact removal
│ └── harden.sh # Post-incident hardening
├── README.md
└── LICENSE
Every run produces:
This tool was built during active incident response against a crypto miner that:
next to blend in with Next.js processes on a Node.js serverkthreadd — an actual kernel thread name/proc/PID/exe → (deleted))ps, top, htop, and btopperf hardware CPU counter profilingMIT
| Step | What it does |
|---|
| ✅ Permissions | chmod +x on miner-hunter and all lib/*.sh scripts |
| ✅ Directories | Creates /var/log/miner_hunter/ and /var/lib/miner_hunter/ (root-only, 700) |
| ✅ Dependencies | Checks perf, mpstat, iptables, fail2ban, bc, strings — auto-installs missing ones |
| ✅ Self-test | Runs ./miner-hunter --version to confirm everything is wired up correctly |
| Command | Description | Changes system? |
|---|
scan | Full detection scan — hidden processes, CPU, network, persistence | ✅ No |
kill | Kill identified miners, block pool IPs, remove artifacts | ⚠️ Yes |
harden | Post-incident hardening — SSH, firewall, watchdog, integrity baseline | ⚠️ Yes |
full | Runs scan → kill → harden with confirmation prompts between phases | ⚠️ Yes |
report | Display the most recent scan report | ✅ No |
| Preview all actions without making any changes |
-e, --evidence DIR | Save evidence to a custom directory instead of /root/miner_evidence_* |
-h, --help | Show help |
-v, --version | Show version |
| Technique | What it catches |
|---|
/proc vs ps comparison | Processes invisible to userspace tools |
| LD_PRELOAD hijacking | Malicious shared libraries hooking libc to hide processes |
| Kernel module rootkits | Diamorphine, Reptile, Kovid, and other known rootkits |
| Fake kernel threads | Miners masquerading as [kworker], [kthreadd], [kswapd] |
| Modified system binaries | Replaced ps, top, ls, ss, netstat |
| Direct kernel-level CPU accounting per PID |
| CPU anomaly detection | High %user CPU with no visible process to explain it |
/proc/net/tcp direct read | Active connections — bypasses hooked ss/netstat |
| Mining port detection | Ports 3333, 4444, 5555, 7777, 9200, 14433, 14444, 45560 |
| Mining domain resolution | Resolves known pool domains and cross-checks active connections |
| Socket-to-PID mapping | Traces which process owns each mining connection |
| Location | What it checks |
|---|
| Cron | /etc/cron*, /var/spool/cron/, all user crontabs |
| Systemd | All unit files and timers for suspicious entries |
| Udev rules | Hardware-triggered execution on device events |
| PM2 | Node.js process manager entries with extreme restart counts |
| Shell profiles | .bashrc, .bash_profile, /etc/profile, /etc/profile.d/* |
| SSH | All authorized_keys files across all users |
| Webshells | PHP files inside Node.js project directories |
| XMRig configs | config.json in common miner drop locations |
| Action | Detail |
|---|
| Firewall persistence | Systemd service to restore iptables mining blocks on every reboot |
| SSH audit | Checks PermitRootLogin, PasswordAuthentication, MaxAuthTries — prints recommended values |
| Fail2ban check | Verifies the sshd jail is active and reports currently banned IPs |
| Miner watchdog | Cron job every 5 min — checks CPU anomaly, LD_PRELOAD, mining ports, PHP webshells |
/usr/bin baseline | MD5 checksums all binaries in /usr/bin for future tamper detection |
| Requirement | Detail |
|---|
| OS | Linux — tested on Ubuntu 24.04 LTS, Debian 13 |
| Privileges | Must run as root (sudo) |
| Auto-installed by setup.sh | perf, mpstat (sysstat), bc, strings (binutils) |
| Recommended | fail2ban — flagged if missing, not auto-installed |
| Required (not auto-installed) | iptables — must be present for kill/harden phases |
| Output | Location | Contents |
|---|
| Evidence directory | /root/miner_evidence_YYYYMMDD_HHMMSS/ | Captured binaries, perf reports, miner configs |
| Log file | /var/log/miner_hunter/run_YYYYMMDD_HHMMSS.log | Full timestamped run log |
| Report | evidence_dir/report.txt | Structured findings summary with severities |
| Watchdog alerts | /var/log/miner_hunter/watchdog_alerts.log | Ongoing alerts after harden |
| Integrity baseline | /var/lib/miner_hunter/usrbin_baseline.md5 | /usr/bin checksums after harden |