Skip to content
Persistencebeginner

Systemd Service & Timer Persistence

Malware installs a systemd unit or timer so init re-launches its payload at boot or on a schedule, giving durable, often root-level persistence on modern Linux.

Systemd is the init system and service manager on most modern Linux distributions. It starts, stops, and supervises services described by unit files, and can restart them automatically if they exit. Malware abuses systemd by installing its own service unit — often with Restart=always — so the payload both survives reboot and is resurrected the moment it is killed, making it a persistence and watchdog mechanism in one.

Systemd timers are the modern replacement for cron and an equally attractive target. A .timer unit fires an associated .service on a calendar schedule or relative to boot, achieving scheduled execution without ever touching crontab. Units placed in a system directory and enabled with a WantedBy install line are wired into a boot target so they start automatically.

How it works

Unit files live in well-known directories that an analyst should enumerate:

text
/etc/systemd/system/        admin-defined units (highest precedence)
/usr/lib/systemd/system/    package-provided units
/etc/systemd/user/  +  ~/.config/systemd/user/   per-user (no root)

A minimal malicious service unit looks like:

text
[Service]
ExecStart=/usr/lib/.cache/dbus-daemon
Restart=always
[Install]
WantedBy=multi-user.target

After dropping the unit the operator runs systemctl daemon-reload && systemctl enable --now <name>, which creates a .wants symlink under the target directory and starts the service. Names mimic legitimate daemons (dbus, networkd-dispatcher, gdm-helper) and the ExecStart path is hidden in a dotted or cache directory. User-level units under ~/.config/systemd/user/ require no root and persist for that account.

Detection & analysis

Static analysis:

  • List enabled units and resolve their files: systemctl list-unit-files --state=enabled plus systemctl list-timers. For anything unfamiliar, read the unit and inspect ExecStart — payloads in /tmp, /dev/shm, dotted, or cache directories are high-signal.
  • Enumerate the unit directories above directly and diff against the distribution's package manifest (rpm -V / dpkg -V); a unit file owned by no package is suspicious. The .wants symlinks under *.target.wants/ reveal what was enabled and when.
  • In a captured sample, look for writes to the systemd unit paths, the strings ExecStart/WantedBy, or execve of systemctl enable/daemon-reload.

Dynamic analysis:

  • Under auditd or strace, watch for open/write to /etc/systemd/system or the user unit directory and for execve of systemctl. Auditd watches on those paths capture the writing process, UID, and the new unit name.
  • At runtime, systemd (PID 1) spawning an unexpected long-running child from a temp or cache path, or a brand-new .service/.timer appearing in systemctl status, is the behavioural tell.

Detection rule hint:

Add auditd watches such as -w /etc/systemd/system -p wa -k systemd_persist (and the user unit dir), then alert on unit writes by non-package processes. Flag enabled units whose ExecStart resolves to a world-writable, temp, dotted, or cache directory, units with Restart=always pointing outside /usr/bin-/usr/sbin, and timers created outside a package install.

Votes

Comments(0)