Persistence after an intrusion often relies on scheduled execution or boot-time autostart so malicious code returns after reboots and logouts. Cron jobs and systemd units are common persistence locations because they blend into routine automation and maintenance.
Cron executes non-interactive commands from per-user crontabs and system-wide schedules such as /etc/crontab, /etc/cron.d, and the /etc/cron.* directories. Systemd starts enabled services at boot and can also trigger services via timer units, where the timer defines the schedule and the service defines the executed command.
Legitimate systems contain many scheduled tasks, so prioritize unknown names, newly added files under /etc/systemd/system, scripts in writable locations (/tmp, /var/tmp, home directories), and commands that launch interpreters or retrieve content over the network. Full enumeration typically requires root access, and timestamps can be misleading during incident response if system time changed or logs rotated.
Related: How to investigate a Linux intrusion
Related: How to list cron jobs in Linux
$ sudo crontab -l # m h dom mon dow command 0 5 * * * /usr/local/bin/backup.sh 15 * * * * /usr/local/bin/rotate-logs.sh
A missing root crontab is common and does not rule out system-wide jobs under /etc/cron.d or /etc/cron.*.
$ sudo crontab -l -u alice # m h dom mon dow command @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 202 Apr 8 2024 e2scrub_all -rw-r--r-- 1 root root 396 Jan 9 2024 sysstat /etc/cron.daily: total 16 -rwxr-xr-x 1 root root 1478 Mar 22 2024 apt-compat -rwxr-xr-x 1 root root 123 Feb 5 2024 dpkg -rwxr-xr-x 1 root root 377 Apr 8 2024 logrotate -rwxr-xr-x 1 root root 518 Jan 9 2024 sysstat /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 if present.
$ sudo grep -R --line-number --extended-regexp --ignore-case '(curl|wget|base64|/dev/shm|/tmp|\.cache)' /etc/crontab /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
No output indicates no matches for the selected patterns.
$ systemctl list-timers --all --no-pager | head -n 8 NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2026-01-13 22:33:14 UTC 6min Mon 2026-01-12 22:35:07 UTC 23h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service - - - - logrotate.timer logrotate.service 2 timers listed.
The ACTIVATES column often points directly at the persistence mechanism.
$ systemctl list-unit-files --type=timer --no-pager | head -n 8 UNIT FILE STATE PRESET apt-daily-upgrade.timer disabled enabled apt-daily.timer disabled enabled dpkg-db-backup.timer disabled enabled e2scrub_all.timer disabled enabled fstrim.timer disabled enabled logrotate.timer enabled enabled motd-news.timer disabled enabled
$ systemctl list-unit-files --type=service --state=enabled --no-pager | head -n 8 UNIT FILE STATE PRESET cron.service enabled enabled demo-startup.service enabled enabled dmesg.service enabled enabled firewalld.service enabled enabled rsyslog.service enabled enabled smartmontools.service enabled enabled ssh.service enabled enabled
Services enabled for autostart are commonly symlinked under /etc/systemd/system/*.wants/.
$ systemctl cat logrotate.service # /usr/lib/systemd/system/logrotate.service [Unit] Description=Rotate log files Documentation=man:logrotate(8) man:logrotate.conf(5) RequiresMountsFor=/var/log ConditionACPower=true [Service] Type=oneshot ExecStart=/usr/sbin/logrotate /etc/logrotate.conf # performance options Nice=19 IOSchedulingClass=best-effort IOSchedulingPriority=7 # hardening options # details: https://www.freedesktop.org/software/systemd/man/systemd.exec.html # no ProtectHome for userdir logs # no PrivateNetwork for mail deliviery # no NoNewPrivileges for third party rotate scripts # no RestrictSUIDSGID for creating setgid directories LockPersonality=true MemoryDenyWriteExecute=true PrivateDevices=true PrivateTmp=true ProtectClock=true ProtectControlGroups=true ProtectHostname=true ProtectKernelLogs=true ProtectKernelModules=true ProtectKernelTunables=true ProtectSystem=full RestrictNamespaces=true RestrictRealtime=true
Stopping or disabling unknown units can break updates, monitoring, and backups; capture evidence and validate ownership before making changes.
$ sudo loginctl show-user alice -p Linger Failed to get user: User ID 1005 is not logged in or lingering
Linger=yes allows per-user systemd units to run without an interactive login.
$ sudo find /home -maxdepth 6 -type f \( -path '*/.config/systemd/user/*.service' -o -path '*/.config/systemd/user/*.timer' \) -print /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 logrotate.service --since '24 hours ago' --no-pager | head -n 6 -- No entries --