Skip to content

Linux

HTB cheatsheet. Technical notes for initial investigation of Linux environments and deciding where to investigate next after gaining access. They distinguish facts confirmed in writeups from mechanisms common to Linux.

These commands include credential extraction, relay, remote execution, persistence, and other features that can have a substantial impact on target systems. Use them only in a test environment or one for which you have explicit authorization.

Pages in this section distinguish four kinds of statements:

  • Confirmed: Supported by a writeup, log, code, or command output.
  • Inferred: Reasonably follows from the evidence, but no direct output was retained.
  • General principle: A general Linux or security mechanism.
  • Unverified: Something that should have been checked but is absent from the available evidence.

Treat general commands as Additional check examples unless a writeup retains a log showing they were run. An Additional check example is not evidence of execution.

Before and after initial access, capture the following relationships in one note:

Externally exposed service
↓
User running the initial access point
↓
Readable configuration, credentials, and internal services
↓
Input path to another user or process
↓
Privilege boundary: sudo / systemd / cron / daemon / socket / container / SUID, etc.
↓
Files, environment variables, arguments, and IPC read by privileged code
↓
Effect on root or the target file

At a minimum, record the following as Confirmed:

  • Current uid, gid, and supplementary groups.
  • Users who can log in, service users, and home directories.
  • Externally exposed ports and localhost services / Unix sockets visible only after access.
  • Users, startup commands, and parent-child relationships of running processes.
  • sudo rules, SUID / SGID, capabilities, systemd units, and cron jobs / timers.
  • Files and directories you can write, and whether privileged processes read them.
  • Inputs, validation, storage, IPC, and dangerous operations in custom code.
  • Files, processes, tunnels, and uploads involved in changes to the target state.

For external investigation, start with Externally Exposed Services. After obtaining a shell, check the executing user and privilege boundaries in this order:

  1. Users and sudo
  2. Processes and systemd
  3. Network and IPC
  4. File and Directory Permissions
  5. Scheduled Execution
  6. Credentials and Configuration
  7. Containers and Namespaces

The privilege escalation pages explain how to interpret boundaries you find:

For traces left by actions and the defender’s perspective, see Logs and Detection.

  1. Run id; whoami; groups to see current privileges and supplementary groups.
  2. Run sudo -l to see the full paths, arguments, and environment restrictions for commands allowed as root.
  3. Use ss -lntup or /proc/net/tcp to find localhost and internal services.
  4. Use ps auxww and /proc/<PID>/cmdline to identify service users and startup arguments.
  5. Use systemctl cat <service> and EnvironmentFile to inspect the actual configuration.
  6. Check writable paths under /opt, /srv, /var/www, and /home/<user>, including parent directory permissions.
  7. Find input directories, logs, sockets, checkpoints, and plugins read by root or another user.
  8. Inside a container, inspect /proc/1, mounts, capabilities, the Docker socket, and internal networks.
  9. Trace data through custom code from its input source to its result.
  10. Before changing state, record what must be cleaned up and which logs to check. See Logs and Detection for the recording format.

Commands to keep handy immediately after gaining access:

Terminal window
id; whoami; groups
sudo -l
ss -lntup
ss -lxnp
ps -eo user,pid,ppid,group,cmd --forest
systemctl cat <service>
systemctl show <service>
tr '\0' ' ' < /proc/<PID>/cmdline
tr '\0' '\n' < /proc/<PID>/environ
ls -l /proc/<PID>/fd
namei -l <path>
stat <path>
findmnt -no TARGET,OPTIONS <path>
getfacl <path>
getcap -r / 2>/dev/null
find /opt /srv /var/www /usr/local -maxdepth 5 -writable -ls 2>/dev/null
cat /proc/self/status | grep Cap
cat /proc/self/mountinfo
  • id / groups: Executing user and supplementary groups.
  • sudo -l: Explicit privilege boundaries.
  • ss and /proc/net/tcp: Internal services invisible from outside.
  • ps and /proc: Executing users, startup arguments, environment variables, and file descriptors.
  • systemctl cat / show: Units, environment files, and working directories actually in use.
  • namei / stat / getfacl: Whether paths can be changed, including their parent directories.
  • findmnt: nosuid, bind mounts, and namespace differences.
  • getcap: File capabilities. For SUID/SGID, use find -perm as shown in File and Directory Permissions.
  • Check the relationships among processes, files, IPC, and service configuration before looking for known vulnerabilities.
  • A localhost service may be reachable from the same host, an SSH tunnel, or a container’s shared network. Identify both the executing user and users who can connect.
  • A privilege boundary exists where a process running as root reads data writable by a lower-privilege user.
  • Distinguish root inside a container, a simulated cloud environment, and the host. Verify boundaries through mounts, capabilities, sockets, and changes to actual files.
  • shell=True, unsafe deserialization, symlinks, module search paths, and archive extraction become problems when privileged code handles input from a lower-privilege user.
  • Do not assume an operation failed solely because of an error response. Check created or changed files, callbacks, and logs.
  • Determine which lower-privilege user’s input a sudo command trusts while running as root.
  • Look beyond login shells in /etc/passwd to service users’ home directories and groups.
  • Even if a command allowed by sudo -l does not launch a shell directly, inspect the data its script reads.
  • Use systemctl cat to inspect the actual unit and distinguish sample configuration from production configuration.
  • Stripping the directory part from a filename alone does not prevent later code from reading an absolute path or another file.
  • Checking only one level of symlink can miss the next level.
  • Failure to read /proc/<PID>/environ or fd is itself information about a privilege boundary.
  • Even without a Docker socket, bind mounts and internal services may provide paths for lateral movement.
  • A root prompt does not mean container root and host root are the same.
  • Evaluate CVE candidates from automated enumeration only after checking the kernel, capabilities, namespaces, and execution conditions.

Keep alternative paths if the evidence supports them. If untested, label them Alternative investigation or Unverified.

  • Use systemd units to identify executing users, environment files, and configuration paths.
  • Use ps and /proc/<PID>/cmdline to distinguish sample configuration from actual configuration.
  • Inspect administrative UIs of localhost services through SSH tunnels or port forwarding.
  • Use find <app-dir> -writable to find plugins, checkpoints, and logs read by root.
  • For Unix domain sockets, check who can connect through stat and group membership, then read the implementation before connecting.
  • Inside containers, check bind mounts and internal services as well as the Docker socket.
  • Do not rely on a vulnerability name alone. Check versions, capabilities, namespaces, mount options, and failure logs.