LinPEAS enumerates systemd services and timers, prints the systemd PATH, and checks unit-file permissions. Writable unit files and relative-path ExecStart entries are the findings that matter, because they let a low-privilege user change what root runs. All testing below assumes an authorised lab or engagement.
Where it appears in the output#
systemd checks are in the procs_crons_timers_srvcs_sockets section, alongside services, sockets and D-Bus. LinPEAS lists units, shows timer schedules, dumps the PATH systemd uses to resolve relative ExecStart paths, and flags unit files and drop-ins that are writable by the current user.
╔══════════╣ Systemd Information
══╣ Systemd PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
══╣ Writable .service files
/etc/systemd/system/backup.service (writable)
══╣ Timers
backup.timer -> backup.service (OnCalendar=daily, runs as root)How to read a systemd finding#
- A writable service/timer file is high priority — you control what root executes the next time the unit runs.
- A relative
ExecStart(no leading/) can be hijacked if any systemd PATH entry is writable — same idea as cron PATH hijack. - Timers are the modern equivalent of cron; treat them exactly the same way — check what they run, as whom, and whether you can write it.
- Drop-in overrides in
/etc/systemd/system/*.d/are easy to miss; LinPEAS checks them, and so should you.
Reproduce the checks manually#
systemctl list-units --type=service --all 2>/dev/null
systemctl list-timers --all 2>/dev/null
systemctl cat backup.service 2>/dev/null # resolved unit, incl. drop-ins
find /etc/systemd/ /lib/systemd/ /run/systemd/ -name '*.service' -writable 2>/dev/null
find /etc/systemd/system/*.d -writable 2>/dev/null # writable drop-insFrom a writable unit to a tested technique (authorised lab)#
In a lab with a root-run service whose unit file you can modify, the proof follows the cron pattern — make the unit do something observable:
- 1
- 2
- 3
- 4
Only on systems you are explicitly authorised to test. Editing service units changes what the system runs as root — on any other machine that is unauthorised access.
How to fix systemd findings (defensive)#
- Unit files and drop-in directories must be root-owned and non-writable (
644files,755dirs). - Use absolute paths in
ExecStart; never rely on PATH resolution. - Set
User=explicitly — services that do not need root should not run as root. - Harden units with
NoNewPrivileges=true,ProtectSystem=strictandReadWritePaths=where the software allows. - Audit timers like cron jobs: what runs, as whom, on what schedule.
- Re-run LinPEAS after hardening to confirm the findings are gone.
User units vs system units#
systemd runs two kinds of units, and the distinction matters for privilege analysis:
| System units | User units | |
|---|---|---|
| Location | /etc/systemd/system/, /lib/systemd/system/ | ~/.config/systemd/user/ |
| Managed by | systemctl (root) | systemctl --user (the user) |
| Runs as | User= or root by default | The owning user |
| Escalation value | High — a writable system unit runs as root | Low — already your privilege level |
| LinPEAS focus | Writable system units, timers, drop-ins | Listed for completeness |
When LinPEAS flags a writable unit, check which kind it is before getting excited — only system units buy you privilege.
Sockets and D-Bus: the quieter surfaces#
LinPEAS also enumerates sockets and D-Bus services in this section. Two things to look for:
- Writable socket units — a service activated by a socket you can trigger runs on your schedule, like a timer you control.
- Exposed D-Bus services — system-bus services with loose policies can be invoked by unprivileged users to perform privileged actions; LinPEAS lists them, you audit the policy.
- Both are rarer than cron/timer findings but higher-value when present, because defenders check them less often.
Auditing timers like cron#
Treat every timer with the same routine you use for cron jobs:
- List them:
systemctl list-timers --all— note schedule, unit and user. - Resolve each timer to its service:
systemctl cat <name>.timer, thensystemctl cat <name>.service. - Check writability of the
.timer, the.serviceand any.d/drop-ins. - Read
ExecStart: absolute path? Which user? What does the binary do? - Check the timer's
PATHhandling the same way you check cron PATH. - Fix like cron: root-owned, non-writable, absolute paths, explicit
User=.
Masked and failed units are signals too#
LinPEAS lists unit states — do not skip the odd ones: a masked unit is deliberately disabled (usually fine, note why), while a failed unit may indicate a crashed security control (firewall, audit daemon) or a half-removed service still referenced by a timer. Failed security-relevant units deserve the same attention as writable ones.
Related: systemd as an escalation surface, PATH risks, cron findings.
Sources & references
- linPEAS README
- PEASS-ng release — reviewed 20260908-dffb9496
- HackTricks: Linux privilege escalation