CHAPTER 04 35 MIN READ INTERMEDIATE

Persistence Mechanisms: Cron, systemd, Shell Profiles & SSH Keys

An attacker who has gained access to a Linux host wants to keep that access even after a reboot, a password reset, or a service restart. On Linux this almost always means abusing one of a handful of well-known startup or scheduling mechanisms. This chapter covers where to look for each one.

cron persistence systemd persistence shell profile persistence SSH key persistence

Cron Persistence

Cron is the classic Linux scheduling mechanism, and it is one of the most common places an attacker plants a recurring command. A new job among dozens of legitimate ones is easy to miss unless someone is looking specifically for it.

LocationNotes
Per-user crontabcrontab -l shows the current user's jobs; each user on the system can have their own crontab
/etc/crontabThe system-wide crontab, readable by anyone but writable only by root
/etc/cron.d/Drop-in system cron jobs, often used by installed packages, which also makes it easy for a malicious entry to blend in
/etc/cron.daily/, /etc/cron.hourly/, etcDirectories of periodic system maintenance scripts, run on a fixed schedule by cron itself

Cron is a common persistence vector because it is simple, ubiquitous, and already trusted. A single extra line in a crontab or a single extra script in a drop-in directory rarely draws attention next to dozens of legitimate entries doing the same thing.

systemd Persistence

systemd service and timer units offer the same recurring or on-boot execution that cron does, but they look like ordinary system infrastructure to anyone who isn't specifically checking. A malicious unit file next to legitimate ones rarely stands out on its own.

LocationScope
/etc/systemd/system/System-wide unit files, applied regardless of which user is logged in
~/.config/systemd/user/Per-user unit files, tied to a specific account

A service unit can be set to start on boot or restart automatically if it stops, giving an attacker resilience beyond a single reboot. A timer unit pairs with a service unit to trigger it on a schedule, achieving the same recurring-execution effect as a cron job while sitting among the system's normal service definitions.

Blending in: a malicious systemd unit sitting among dozens of legitimate service files is easy to overlook without comparing the unit list against a known-good baseline of what should normally be present.

Shell Profile Persistence

Shell profile files run automatically whenever a matching shell starts, which means code planted in one of them executes without needing to trick the user into running anything themselves.

FileTriggers On
~/.bashrcInteractive non-login shell, such as opening a new terminal window
~/.bash_profile or ~/.profileLogin shell, such as an SSH session or a fresh console login
/etc/profile.d/System-wide scripts sourced for all users at login

Because these files execute silently on ordinary, everyday actions like opening a terminal or logging in over SSH, a single planted line can re-establish an attacker's foothold every time the legitimate user simply works normally.

SSH Key Persistence

~/.ssh/authorized_keys lists the public keys allowed to log in as that user without a password. An attacker who can write to this file adds their own public key, and that access does not depend on the account's password at all.

That independence from the password is what makes this mechanism durable: resetting the user's password, or even forcing a full credential rotation elsewhere, does nothing to remove a key an attacker already planted in authorized_keys.

Investigating it: comparing the authorized_keys file against what the legitimate user actually set up, and checking the timestamps of when each key was added, is a standard part of investigating suspected SSH-based persistence.

Persistence Hunting Checklist

Each mechanism in this chapter is easy to check individually, but attackers often rely on defenders checking only one. Working through all of them, in order, is what turns a spot check into a real sweep.

1
Check Cron
Review per-user crontabs, /etc/crontab, and /etc/cron.d/ for entries that don't belong.
2
Check systemd Units and Timers
List service and timer units in both the system and per-user locations.
3
Check Shell Profile Files
Inspect .bashrc, .bash_profile, .profile, and /etc/profile.d/ for planted commands.
4
Check authorized_keys
Review every user's ~/.ssh/authorized_keys for keys the account owner didn't add.
5
Compare Against a Known-Good Baseline
Diff everything found against what the system is supposed to look like, not just against intuition.

Once these mechanisms have been checked, the next step is looking at what they may have already launched, which is where chapter 5 picks up with live process and network state.

Key Takeaways

  • Cron persistence can live in per-user crontabs, /etc/crontab, /etc/cron.d/, or the periodic cron.daily/cron.hourly directories.
  • systemd service units and timer units can start on boot or run on a schedule, giving the same effect as cron while looking like ordinary system infrastructure.
  • Shell profile files (.bashrc, .bash_profile, .profile, /etc/profile.d/) execute automatically on normal actions like opening a terminal or logging in.
  • .bashrc triggers on interactive non-login shells, while .bash_profile and .profile trigger on login shells.
  • A planted key in ~/.ssh/authorized_keys survives a password reset, because it doesn't rely on the password at all.
  • Hunting persistence means checking cron, systemd, shell profiles, and authorized_keys together, then comparing all of it against a known-good baseline.

Knowledge Check

Click an answer to reveal the explanation.

What is /etc/cron.d/ typically used for?

Correct answer: B. /etc/cron.d/ holds drop-in cron job files, commonly created by installed packages, which also makes it a convenient place for a malicious entry to blend in.

Why does a planted key in ~/.ssh/authorized_keys survive a password reset?

Correct answer: B. SSH key authentication is independent of the account's password, so resetting the password has no effect on a key an attacker already added to authorized_keys.

When does ~/.bashrc trigger, as opposed to ~/.bash_profile?

Correct answer: B. ~/.bashrc runs for interactive non-login shells, like opening a new terminal window, while ~/.bash_profile or ~/.profile runs for login shells, like an SSH session.