Loading...
Home
Explore
Contact
Sign in
Linux

Find and Delete Hidden Backdoors & Malicious Cron Jobs on Linux

Spot rogue crons, SSH key implants, and reverse shells on your Linux server—then remove them safely with copy-paste commands, verification checks, and clear guidance on when Fixwebnode should take over.

Fixwebnode Support
Fixwebnode Support
10 min read 7 views
Find and Delete Hidden Backdoors & Malicious Cron Jobs on Linux

If your Linux VPS or small-business server feels “owned”—odd CPU spikes, mystery outbound connections, files that reappear after you delete them—you need a methodical hunt for hidden backdoors and malicious cron jobs, not guesswork.

This guide walks homeowners, agencies, and SMB operators through practical checks on common distributions (Ubuntu, Debian, Alma/RHEL-family). You will inventory cron and systemd timers, audit SSH persistence, catch reverse shells and web shells, and verify the box is clean. When the trail goes deeper than DIY, Fixwebnode server support can finish the cleanup and harden the host so the same implant does not return.

Why hidden backdoors and malicious crons matter on Linux servers

Attackers rarely leave a single obvious binary. They chain persistence: a cron line that curls a payload every five minutes, an extra key in authorized_keys, a systemd user timer, or a PHP web shell under a theme directory. From a small shop site to a home lab exposed on the public internet, the symptoms look the same—reboots that “fix” nothing, mail queues full of spam, or ss showing connections you never opened.

DIY is appropriate when you still have root, logs are intact, and you can take a short maintenance window. It is not appropriate when ransomware is active, customers’ data may already be exfiltrated, or every cleanup step is undone within minutes. Fixwebnode works across Australian service areas and remote Linux estates; see all service areas if you need on-call coverage beyond a single city.

Common issues you will actually see

These problems are distinct. Treat each as its own investigation track so you do not miss a second persistence path while fixing the first.

  • Issue 1 — Malicious or obfuscated cron jobs that reinstall malware. Symptoms: high load at fixed intervals, unknown scripts under /tmp, /var/tmp, or /dev/shm, and jobs that reappear after you delete a binary. Often base64-wrapped one-liners in root or www-data crontabs.
  • Issue 2 — Hidden SSH backdoors via authorized_keys and unexpected users. Symptoms: successful logins from unfamiliar IPs in auth.log/secure, extra keys with no comment matching your team, or new local users with UID 0 or sudo rights.
  • Issue 3 — Reverse shells, rogue processes, and systemd timer implants. Symptoms: long-lived bash -i, nc, python -c, or curl|sh processes; odd listening ports; user-level systemd units that survive reboot without a classic cron entry.
  • Issue 4 — Web shells and droppers in document roots. Symptoms: POST spikes to single PHP/ASP files, files with recent mtimes in upload or cache folders, and outbound HTTP from the web user even when the site is idle.

Issue 1 — Find and remove malicious cron jobs

Cron is still the most common “set and forget” persistence on compromised Linux hosts. Check every user and every system drop-in directory—not only crontab -l as root.

Step 1 — List every crontab and system cron directory

sudo crontab -l
sudo ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly
sudo grep -RsnE '.+' /etc/cron.d/ /etc/crontab 2>/dev/null
for u in $(cut -f1 -d: /etc/passwd); do echo "===== $u ====="; sudo crontab -u "$u" -l 2>/dev/null; done

Flag lines that download remote content (curl, wget, fetch), pipe to shells, write under /tmp, or call filenames that look random.

Step 2 — Inspect at and systemd timers (cron alternatives)

sudo atq
systemctl list-timers --all
systemctl list-unit-files --type=timer
ls -la /etc/systemd/system /etc/systemd/system/*.timer /var/spool/cron 2>/dev/null

Step 3 — Quarantine, do not only delete blindly

Copy suspect scripts aside, then remove the job so you keep forensic evidence:

sudo mkdir -p /root/incident-quarantine/$(date +%F)
sudo cp -a /path/to/suspect-script /root/incident-quarantine/$(date +%F)/
sudo crontab -e
# or edit the matching file under /etc/cron.d/
sudo systemctl disable --now suspicious.timer 2>/dev/null
sudo rm -f /etc/systemd/system/suspicious.timer /etc/systemd/system/suspicious.service
sudo systemctl daemon-reload

Step 4 — Verify the schedule is gone

sudo grep -RsnE 'curl|wget|base64|/tmp/|/dev/shm' /etc/cron* /var/spool/cron 2>/dev/null
systemctl list-timers --all
ps auxf | head -n 50

Watch CPU and network for one full cron cycle (often 5–15 minutes). If the payload returns, another persistence path still exists—continue with Issues 2–4 rather than looping on the same file.

When to call Fixwebnode: jobs rewrite themselves after removal, root’s crontab is empty but load still spikes on a schedule, or you cannot tell legitimate backup crons from hostile ones on a busy host.

Issue 2 — Hunt SSH key backdoors and rogue accounts

Password lockouts do nothing if an attacker already dropped a public key. Audit keys and users before you rotate anything else.

Step 1 — Inventory authorized_keys for every home and system account

sudo find /root /home /etc -name authorized_keys -type f 2>/dev/null
sudo sh -c 'for f in $(find /root /home -name authorized_keys 2>/dev/null); do echo "FILE $f"; cat "$f"; echo; done'

Remove keys you do not recognize. Prefer deleting the whole line over commenting if the key is confirmed hostile.

Step 2 — Find unexpected users, UID 0 clones, and sudoers drift

awk -F: '($3 == 0) { print }' /etc/passwd
getent passwd | awk -F: '$3 >= 1000 { print }'
sudo grep -RsnE '.' /etc/sudoers /etc/sudoers.d 2>/dev/null
last -a | head -n 40
sudo grep -E 'Accepted|Invalid user|session opened' /var/log/auth.log 2>/dev/null | tail -n 80
sudo grep -E 'Accepted|Invalid user' /var/log/secure 2>/dev/null | tail -n 80

Step 3 — Lock down SSH configuration

sudo cp -a /etc/ssh/sshd_config /root/incident-quarantine/sshd_config.bak
sudo grep -E '^(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|AuthorizedKeysFile|AllowUsers|Port)' /etc/ssh/sshd_config
# After edits you trust:
sudo sshd -t && sudo systemctl reload sshd || sudo systemctl reload ssh

Disable password auth only after your own key works in a second session. Kill unknown sessions:

who
sudo ss -tnp | grep ':22'
# identify PID then: sudo kill PID

Step 4 — Verify

sudo find /root /home -name authorized_keys -exec wc -l {} \;
sudo tail -n 50 /var/log/auth.log 2>/dev/null
sudo tail -n 50 /var/log/secure 2>/dev/null

When to call a pro: you see multiple UID 0 accounts, host keys were replaced, or bastion/jump-host trust means one bad key unlocks a fleet. Pair deep SSH cleanup with broader hardening via Secure Linux Admin & Full-Stack Infrastructure Australia when the server is part of a multi-host stack.

Issue 3 — Catch reverse shells, rogue processes, and timer implants

If cron looks clean but the box still phones home, inspect live process trees, sockets, and user systemd units.

Step 1 — Process and socket triage

ps auxf
ps -eo pid,ppid,user,cmd --sort=start_time | tail -n 40
sudo ss -tulnp
sudo ss -tnp state established
sudo lsof -i -n -P 2>/dev/null | head -n 100

Suspicious patterns include interpreters with -c payloads, nc -e, /dev/tcp/ bash redirects, mining binaries, and listeners on high ports bound to 0.0.0.0 that you never configured.

Step 2 — Map executable path and kill cleanly

sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
sudo kill PID
# if it ignores SIGTERM:
sudo kill -9 PID

Quarantine the binary path shown by /proc/PID/exe before deletion if the file still exists on disk.

Step 3 — User and system systemd persistence

systemctl list-units --type=service --all | grep -viE 'loaded|active \(running\) .* (ssh|cron|nginx|apache|mysql|postgres)'
ls -la /etc/systemd/system /usr/lib/systemd/system
sudo find /home /root -path '*/.config/systemd/user/*' 2>/dev/null
sudo find /var/spool/cron /var/spool/at -type f 2>/dev/null

Disable and remove unknown units, then daemon-reload. Reboot into a maintenance window if processes respawn faster than you can kill them.

Step 4 — Verify no callback remains

sudo ss -tnp state established
ps aux | grep -E 'nc|ncat|bash -i|python|perl|ruby|curl|wget' | grep -v grep
sudo journalctl -xe --since "1 hour ago" | tail -n 100

When to call Fixwebnode: the parent process is a legitimate service (web, DB) that keeps re-spawning the shell, kernel modules look tampered, or you need packet capture and IOC extraction without taking production down longer than necessary.

Issue 4 — Locate and remove web shells and droppers

Shared hosting-style layouts and CMS installs are frequent drop zones. Work from the document root outward.

Step 1 — Find recently changed and odd web files

sudo find /var/www /srv /home/*/public_html -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.php*' \) -mtime -14 -ls 2>/dev/null
sudo find /var/www -type f -name '*.php' -size -50k -exec grep -lE 'eval\s*\(|base64_decode\s*\(|gzinflate|shell_exec|passthru|assert\s*\(' {} \; 2>/dev/null

Step 2 — Quarantine matches and fix ownership

sudo mkdir -p /root/incident-quarantine/web
sudo mv /var/www/html/path/to/shell.php /root/incident-quarantine/web/
sudo chown -R root:root /root/incident-quarantine
sudo find /var/www -type f -name '*.php' -perm -0002 -ls

Restore clean copies from known-good backups or vendor packages. Rotate CMS and database credentials after removal.

Step 3 — Confirm the web user is quiet

sudo ss -tnp | grep -E 'nginx|apache|httpd|php-fpm'
sudo tail -n 100 /var/log/nginx/access.log 2>/dev/null
sudo tail -n 100 /var/log/apache2/access.log 2>/dev/null
sudo tail -n 100 /var/log/httpd/access_log 2>/dev/null

When to call a pro: the site is a multi-vhost production box, malware reinfects on every deploy, or package metadata is broken while you try to reinstall clean packages—for example corrupted APT configuration that blocks security updates. In that case use specialist help such as Fix Corrupted Linux APT Sources.list - Alabama Expert Support alongside malware removal so patching works again.

When DIY is enough vs when to book Fixwebnode

DIY is enough when you retain exclusive root access, you can snapshot or back up first, only one persistence mechanism shows up, and after cleanup a full timer cycle plus reboot shows no rogue processes, no unknown keys, and no hostile cron lines. Document every file you moved into /root/incident-quarantine.

Book Fixwebnode when any of the following is true: persistence returns after a clean reboot; you suspect lateral movement to other servers; customer PII may have left the host; disk rootkits or unexpected kernel modules appear; you cannot safely rotate secrets alone; or you need a hardened baseline (SSH, firewall, unattended upgrades, monitoring) applied consistently after the incident. Australian small businesses and remote operators can start from the same runbook above, then hand off for verification and lock-down rather than living on a half-cleaned host.

Practical handoff checklist to speed support: note distro and version (cat /etc/os-release), whether the host is bare metal or VPS, time the odd behavior started, outputs of crontab/ss/authorized_keys searches, and whether a snapshot exists.

Close the loop and get help if you need it

Hidden backdoors on Linux almost always combine schedule (cron/timers), access (SSH keys/users), and execution (shells or web droppers). Work those three layers in order, quarantine instead of silent delete, and verify with process, socket, and log checks after every change.

If you want a specialist to finish the hunt, validate the clean state, and harden the server so the next scan is boring, start a conversation through Fixwebnode server support. Bring the quarantine folder and command outputs from this guide—you will spend less time on triage and more time back on a trustworthy host.

Share this article
Fixwebnode Support
Fixwebnode Support

Hey there!
I am your assistant for Fixwebnode. Ask about our services, quotes, packages, orders, or how to get support.
While you wait
What’s your name and best email? We’ll reply even if you leave.