Skip to content

Repository files navigation

AUTOPRIV banner

AUTOPRIV

Automated Linux + Windows privilege escalation suite — scan, enumerate, auto-root.
One Go binary. Zero dependencies. Honest results.

CI Go 1.26 Release MIT License 185 tests passing

AUTOPRIV real scan: banner, findings and hardening score

CI regression gate: baseline, a secret lands, --fail-on-new=high trips with exit 3

Real terminal output — no mockups. More media below: the HTML report, the hardening playbook and the vector catalog.


What is AUTOPRIV?

AUTOPRIV is a post-exploitation tool for authorized Linux security work: labs, CTFs and systems you own. You drop the single static binary on a box, it audits the machine read-only in seconds, and it tells you — with evidence — every local privilege-escalation path it found. If you ask it to, it walks those paths for you, always from the safest technique to the most destructive one, and it stops the moment it reaches uid=0.

It is built for two very different moments. In an engagement, it is the fastest way to answer "can this box go root, and how?". In a study session, it is a teacher: every finding explains the vector, the risk and the exact command an operator would run, so you can reproduce it manually and actually learn the technique.

Everything is deliberate about its honesty. Vectors it cannot verify automatically are marked manual instead of pretending. Kernel CVE matches say verify before use (heuristic). When it reaches root it prints uid=0 evidence from the shell itself, and when it does not, it says so and exits with code 1.

Highlights

Linux: 20 read-only scanners SUID/SGID, sudo rules + version, writable cron (+ wildcard-injection candidates), passwd/shadow injection, docker group, container runtime context (podman/containerd/docker daemon), capabilities (both bitmask and file caps), NFS (no_root_squash + hostless-rw exports), writable PATH dirs, systemd units AND init.d scripts, kernel CVEs, credentials in history/configs, cloud metadata, ld.so.preload + writable ld.so.conf(.d), writable sudoers (file, dir and per-file drop-ins), writable /etc/group, writable login hooks (/etc/environment, /etc/profile.d, /etc/profile, /etc/bash.bashrc), writable polkit policy surfaces (rules.d, .rules files, localauthority dirs)
Windows: 8 read-only scanners (v1.9) token privileges (SeImpersonate → potato family, SeBackup, SeDebug — Priv2Admin primitives), UAC-filtered-admin surface, registry misconfigurations (AlwaysInstallElevated, plaintext AutoLogon, EnableLUA), service attack surface (unquoted paths with spaces, user-writable binary dirs, SYSTEM services running user-profile binaries), autorun keys, scheduled tasks with writable commands, credential artifacts (unattend/sysprep, GPP cpassword, PowerShell history, cloud/SSH keys), PATH hijacks
75 GTFOBins techniques + 31 sgid embedded in the binary — works air-gapped; refreshable from upstream with one command (--list-gtfo shows the sgid section)
Hardening score every scan ends with a deterministic 0–100 posture number — weighted by risk and exploitability — so --baseline diffs read score 60 → 85 instead of raw counts
Hardening playbook --explain cron (or all) prints the exact remediation steps per finding source; --report embeds a ## Hardening plan section built from the sources actually detected
Safest-first auto-exploit Linux: techniques sorted by risk, --risk cap, --one-shot stop at first root. Windows (v1.9): read-only enumeration with the exact manual command per technique (--dry-run shows the plan)
Five output formats human terminal with truecolor ramp, --json for machines (--output file persists it, 0600), markdown --report with evidence, self-contained --html page for stakeholders, and --sarif for GitHub/GitLab code-scanning dashboards
Machine-readable catalogs --list-vectors --json, --list-sources (text or --json) and --explain cron --json speak the same envelope (tool/version/mode) so dashboards can ingest the vocabulary the binary itself uses — canonical order, zero hardcoded drift
Two CI gates → three --fail-on risk fails when the surface EXISTS at/above a risk; --fail-on-new (optional threshold: --fail-on-new=high) fails only when it GROWS — the regression verdict for progressive-hardening pipelines; --min-score n fails while the posture number sits under the floor
Rootless demo lab lab/rootless_lab.sh builds a fake-vulnerable box inside a user namespace — no Docker, no real root, nothing touches your system
Script-friendly --quiet + exit codes (0 root, 1 no root, 2 error, 3 policy gate), --no-color auto when piped, --color to force the terminal look through a pipe (captures, demos), NO_COLOR respected, --parallel cuts scan wall-clock with byte-identical results, --top n sizes the end-of-run vector list

How it works

1 — Scan. Every scanner runs read-only and emits findings with risk labels: [.] informational, [+] exploitable, [!] notable. The scan phase never modifies the machine — it reads sudoers, cron, passwd/shadow, mounts, capabilities, kernel version, shell history and config files.

2 — Enumerate. Findings become vectors. Each vector gets a concrete technique and the exact command that would be run, pulled from the embedded GTFOBins database where applicable (SUID python3, find, vim, ...). Vectors that need a human decision are flagged manual.

3 — Exploit (opt-in). With --exploit, vectors are sorted safest-first and executed under the risk cap. The session ends the moment uid=0 is confirmed — or after the list is exhausted, with an honest no root and exit code 1.

The whole pipeline, one glance:

AUTOPRIV pipeline: scan, filter, enumerate, exploit, report+score, CI gates, exit codes

Quick start

Download a release binary (linux amd64/arm64, windows amd64/arm64 — statically linked, see docs/DEPLOYMENT.md for the full requirements matrix):

# Linux
curl -LO https://github.com/Ruby570bocadito/Auto-Privilege/releases/latest/download/autoprivilege-linux-amd64
chmod +x autoprivilege-linux-amd64 && mv autoprivilege-linux-amd64 autoprivilege
./autoprivilege --help
# Windows 10 1809+ / Server 2019+
curl -LO https://github.com/Ruby570bocadito/Auto-Privilege/releases/latest/download/autoprivilege-windows-amd64.exe
.\autoprivilege-windows-amd64.exe --help

Or build from source (Go 1.26+, no module dependencies to fetch — cross-compiles to every target from any OS):

git clone https://github.com/Ruby570bocadito/Auto-Privilege.git
cd Auto-Privilege
go build -o autoprivilege ./cmd/autoprivilege              # native
GOOS=windows go build -o autoprivilege.exe ./cmd/autoprivilege   # windows cross-compile

Or just try it in the safe lab first:

lab/rootless_lab.sh                  # read-only scan of a fake vulnerable box
lab/rootless_lab.sh --exploit        # watch it climb to uid=0 in a namespace

Usage

autoprivilege [options]

Modes:
  (default)                 scan + enumerate (read-only, no changes)
  --exploit                 auto-exploit found vectors, safest first
  --dry-run                 scan + enumerate, show what would run
  --list-gtfo               print the embedded GTFOBins database
  --list-vectors            print the supported vector catalog
  --list-sources            print the finding-source vocabulary (--ignore/--explain)
  --completion shell        print a shell completion script: bash, zsh or fish
  --explain src             hardening playbook for a finding source (or all)
  --update-gtfobins         refresh GTFOBins db from upstream (persisted)

Targeting:
  --vector list             comma-separated: suid,sgid,sudo,cron,passwd,shadow,
                            docker,container,caps,nfs,path,service,kernel,cred,
                            preload,sudoers,group,hooks,polkit,all
  --risk level              max auto-exploit risk: safe|low|medium|high|danger
  --one-shot                stop after the first successful exploit
  --lhost ip                reverse-shell listener host (auto-detected)
  --lport port              reverse-shell listener port (default 4444)

Output:
  --json                    machine-readable report on stdout
  --output file             write the JSON report to a file (0600)
  --report file             also write a markdown evidence report
  --html file               write a self-contained HTML report (0600)
  --sarif file              SARIF 2.1.0 report for code-scanning dashboards
  --sarif-stdout            print the SARIF log to stdout (not with --json)
  --baseline file           diff findings against a previous --json/--output report
  --quiet                   no output; exit code 0 = root, 1 = no root
  --no-color                disable ANSI colors (auto-off when piped)
  --color                   force ANSI colors even when piped (captures, demos)
  --verbose                 debug logging on stderr
  --log fmt                 log format: text|json (stderr)

Misc:
  --ignore list             exclude finding sources entirely: e.g. CRED,CONTAINER
  --min-risk level          hide findings below this risk floor:
                            low|medium|high|danger — the floor applies
                            everywhere (terminal, JSON, reports, gates)
  --parallel                run scanners concurrently (same results, faster)
  --stealth                 jitter between scanners and exploits
  --scan-timeout dur        timeout for scan-time external commands (default 5s)
  --fail-on risk            exit 3 when exploitable findings >= risk
                            (low|medium|high|danger) — CI hardening gate
  --fail-on-new [risk]      exit 3 when NEW exploitable findings appear vs
                            --baseline — regression gate (requires it);
                            optional threshold: --fail-on-new=low|medium|high|danger
  --min-score n             exit 3 when the hardening score lands below the
                            floor (1-100) — posture gate; 0 disables it
  --top n                   show the top n vectors after a failed exploit
                            run (default 5, max 50)
  --rooteame path           load .ko module if root is obtained (lab only)
  --version                 print version
  -h, --help                this help

Exit codes: 0 root obtained · 1 no root · 2 usage or runtime error · 3 a CI gate tripped — --fail-on policy, --fail-on-new regression or --min-score posture (gates win over 1; all reports are written either way). The full decision map, evaluated last in this order:

AUTOPRIV exit-code decision map: 2 usage, 1 no root, 3 gates win, 0 clean

Not sure what --vector accepts? --list-vectors prints the catalog — every vector name with a one-line description of what it actually inspects, read from the same table the binary itself uses, so the docs can never drift from the code. Add --json and the same catalog arrives as a machine-readable document with a tool/version/mode envelope. The sibling --list-sources does the same for the finding-source vocabulary (--ignore/--explain accept): 26 sources (18 Linux + 8 Windows), each described by the exact sentence its hardening playbook uses — and its --json voice feeds dashboards without scraping help text:

--list-vectors: the 26-vector catalog

--list-sources: the 26-source vocabulary (18 Linux + 8 Windows), text and --json voices

Vectors covered

Vector What it checks Auto?
suid SUID binaries (recursive walk) + GTFOBins match (python3, find, ...) yes
sgid SGID binaries with root group — group-level escalation, manual vector (GTFOBins sgid technique preferred; SUID fallback declared in the note) manual
sudo sudo -l rules, NOPASSWD entries, sudo version CVEs (Baron Samedit range) partial
cron writable /etc/cron*, PATH cron jobs, wildcard-injection candidates in root schedules (tar/rsync/zip/7z with * arguments) yes
passwd writable /etc/passwd — root user injection yes
shadow readable /etc/shadow — hash extraction yes
docker docker group / socket access → host root partial
container inside-container indicator with privilege/PID-namespace evidence, podman/containerd sockets, reachable docker daemon partial
caps cap_setuid processes, file capabilities (getcap -r /) yes
nfs no_root_squash exports (exploitable) and rw exports with no host restriction (informational) manual
path writable dirs in root's PATH yes
service writable systemd units AND SysV init.d scripts (both execute as root at boot/restart) yes
kernel kernel-range CVEs: Dirty Pipe, Dirty Cow, OverlayFS, StackRot, nf_tables; PwnKit via pkexec partial
cred passwords in history, configs, cloud metadata (imds, 800 ms timeout) yes
preload /etc/ld.so.preload non-empty (loaded with euid 0 into every SUID binary) and writable ld.so.conf/ld.so.conf.d (library search paths absorbed by the next ldconfig) — HIGH/exploitable when writable, informational otherwise manual
sudoers writable /etc/sudoers (append NOPASSWD rule, auto) or writable /etc/sudoers.d (drop-in, manual: sudo requires root-owned files); per-file pass surfaces root-owned writable drop-ins behind a locked dir partial
group writable /etc/group — append yourself to sudo/wheel/docker, effective next login manual
hooks writable login-time hooks: /etc/environment (LD_PRELOAD into every session), /etc/profile.d, /etc/profile, /etc/bash.bashrc manual
polkit writable polkit policy surfaces: rules.d directory (auto — plants a temporary 00- rule granting org.freedesktop.policykit.exec, runs pkexec, removes the rule), writable .rules file or localauthority dir (manual — the exact grant, human confirms) partial

Windows vectors (v1.9) — enumerated by the native Windows scanners, every technique printed as the exact manual command:

Vector What it checks Auto?
winpriv token privileges: SeImpersonate → potato family (PrintSpoofer/GodPotato/JuicyPotatoNG), SeBackup (SAM/SYSTEM hive dump), SeDebug, SeLoadDriver, SeTakeOwnership, SeRestore, SeCreateToken, SeTcb (Priv2Admin primitives); UAC-filtered-admin surface (medium integrity + Administrators) manual
winreg AlwaysInstallElevated in BOTH hives (any .msi installs as SYSTEM), plaintext AutoLogon password, EnableLUA=0 (UAC off), saved PuTTY sessions manual
winservice unquoted service paths with spaces (binary-planting chain, writable-ancestor checked), service binaries in user-writable directories, SYSTEM services running user-profile binaries manual
winauto Run/RunOnce autorun keys (HKLM/HKCU + Wow6432Node) whose binary or directory the current user can rewrite manual
wintask scheduled tasks executing user-writable commands, privileged tasks with user-profile scripts manual
wincred unattend/sysprep answer files with password fields, GPP cpassword (MS14-025 — public AES key), PowerShell history, AWS/Azure/SSH key material manual
winpath writable directories in the Windows PATH — binary/DLL hijack of privileged resolutions manual

partial means AUTOPRIV sets the stage (version checks, rule parsing) but a human confirms the final step — the tool says so instead of faking it. On Windows every vector is manual by design: v1.9 enumerates and prints the exact technique (payload generation, registry reads, binary planting), and the exploit phase reports an honest "Linux-only" instead of pretending.

The docker/container probes inherit your shell environment, so a daemon configured via DOCKER_HOST (remote or local) counts as reachable — the finding says "verify rootful vs rootless" because a rootless daemon contains the classic breakout.

Windows support (v1.9)

The same binary source builds and runs natively on Windows (windows/amd64, windows/arm64): the platform layer (platform_windows.go + windows_scan.go) owns every OS-shaped question, and the CI runs a real Windows job — build, vet, the full test suite and a live scan whose --json contract is validated on the runner.

What works on Windows:

  • All 8 WIN scanners* (see the vector table above) — read-only, using only built-ins (reg.exe, whoami.exe, PowerShell CIM), each bounded by --scan-timeout.
  • The complete reporting pipeline: terminal, --json, --report, --html, --sarif, hardening score, --baseline diff, the three CI gates, --explain/--ignore/--list-vectors/--list-sources/--completion.
  • ANSI colors in Windows Terminal natively, and in legacy conhost via the ENABLE_VIRTUAL_TERMINAL_PROCESSING flip the binary performs on startup (--no-color always available).

What stays Linux-only in v1.9: auto-exploitation (--exploit) and the 20 Linux scanners (SUID, sudo, cron, systemd, polkit, …) — the Windows phase says so instead of silently no-op'ing. The one deliberate probe exception to "read-only": directory-writability is tested by creating and deleting a 0-byte temp file (ACLs cannot be read from permission bits on Windows — winPEAS probes the same way).

Deployment requirements for every platform live in docs/DEPLOYMENT.md.

Output formats

Terminal — colorized findings with per-line evidence and a final summary (real output of the rootless lab; inside the user namespace the tool starts as mapped root, hence rooted YES):

  ── Findings ──
  [.] SUID → SUID binary: find (GTFOBins: true) (/usr/bin/find)
  [!] SUID → SUID binary: python3.13 (GTFOBins: true) (/usr/bin/python3.13)
  [!] CRON → Writable cron job — inject command (/etc/cron.d/backup)
  [!] FILE → Writable /etc/passwd — inject root user (/etc/passwd)
  [!] FILE → Readable /etc/shadow — crack root hash (/etc/shadow)
  [*] FILE → Writable /etc/shadow — set root password (/etc/shadow)
  [~] CAPS → Process holds CAP_SETUID — can become root in-process (cap_setuid)
  [!] SERVICE → Writable systemd service — hijack execution (/etc/systemd/system/vuln.service)

  ── Summary ─────────────────────────────
   findings   12  (exploitable 9)
   score      31/100
   vectors    9  (auto 6 · manual 3)
   risks      LOW 3  MEDIUM 1  HIGH 7  DANGER 1
   rooted     YES
   time       1.7s

JSON (--json) — one object on stdout, ready for jq (lab progress goes to stderr, so the pipe is clean). With --dry-run, the object gains a structured "plan" key: the exact execution plan an --exploit run WOULD follow, safest first, each entry with name/risk/target/command/kind (auto|manual) and the within_risk verdict against the current --risk cap — CI can preview the whole climb without executing anything:

$ lab/rootless_lab.sh --json --quiet | jq '.summary'
{
  "findings": 12,
  "exploitable": 9,
  "score": 31,
  "vectors": 9,
  "auto": 6,
  "manual": 3,
  "risks": { "DANGER": 1, "HIGH": 7, "LOW": 3, "MEDIUM": 1 },
  "rooted": true
}

Markdown (--report audit.md) — a summary table with the at-a-glance counts (findings, exploitable, hardening score, auto/manual vectors, risk distribution), per-vector sections with the command, the risk and the evidence lines, and a ## Hardening plan section: the exact remediation playbook for every source the scan actually detected. Suitable as an engagement appendix that carries its own checklist.

HTML (--html audit.html) — the shareable page: one self-contained document (inline CSS, zero external resources, no JavaScript) that opens from file:// on any laptop, air-gapped included. Summary cards, the risk-distribution bar, the findings table with color-coded badges, the hardening plan as collapsible per-source playbooks and every vector command in a copy-ready block — all rendered from the same buildReport the JSON export produces, so the page can never disagree with the machine output. Every dynamic value is HTML-escaped (a credential file containing <script> renders as text, tested) and the file lands 0600 through the same atomic write as the other reports. Attach it to the engagement doc and send it:

The --html report opened in a browser: summary cards, risk bar and findings table

SARIF (--sarif audit.sarif) — the findings dressed as a SARIF 2.1.0 log, the format GitHub's code-scanning tab, GitLab and every SARIF viewer ingest natively: one rule per finding source, error/warning/note levels mapped from the risk scale, file:// locations for path targets (non-path targets like ALL or CVE ids embed the target into the message instead). Written 0600 like every other report artifact. Upload it with github/codeql-action/upload-sarif@v3 and the scan shows up in the Security tab — no converter, no external dependency.

Baseline diff (--baseline prev.json) — the hardening loop, closed: snapshot a machine with --output base.json, fix what you can, re-scan against the snapshot and the tool classifies every finding as new (surface grew) or resolved (fix worked), keyed by source+target so reordering or rewording never fakes a change. The diff carries the hardening-score trajectory (score 60 → 85; baselines written before the metric existed render baseline predates scoring instead of faking a regression) and appears in the terminal, in the JSON ("diff" key with new/resolved/new_exploitable/score_after/summary_before) and in a ## Diff vs baseline section of the markdown report:

$ autoprivilege --output base.json          # day 0: snapshot
$ # ... harden the machine ...
$ autoprivilege --baseline base.json        # day N: verify
  ── Diff vs baseline ─────────────────────
   new        1  (exploitable 1)
   resolved   3
   score      60 → 85

The same diff section renders in the HTML report — baseline path and date, the new/resolved counts, the score trajectory and a badge table of exactly the new findings:

The Diff vs baseline section of the --html report

Risk floor (--min-risk medium) — the noise dial for loud hosts: findings below the floor stop existing for the whole run, applied ONCE right after the scan under the same contract as --ignore — the terminal, the JSON, the reports, the hardening score and all three gates see the identical filtered set, so a gate can never trip on something the floor hid. The floor is inclusive (medium keeps MEDIUM and above), safe is rejected with a hint (the default already shows everything), and the same scan reads 17 findings, score 40 without it and 4 findings, score 66 with it:

--min-risk medium: 17 findings drop to 4, hardening score climbs 40 to 66, jq sees the same

CI hardening gate (--fail-on) — turn the scan into a policy check: --quiet --fail-on high exits 3 when at least one exploitable finding sits at or above the threshold, so a pipeline (or a cron job shipping reports) fails loudly the moment the measured surface regresses. Regression gate (--fail-on-new [risk], requires --baseline) — the sharper CI verdict: exit 3 only when a NEW exploitable finding appears versus the snapshot, so hardening progress never fails the pipeline and a regression always does. The optional threshold narrows the verdict: bare --fail-on-new trips on any new exploitable finding, --fail-on-new=high only when the regression is HIGH or worse — noisy low-risk drift stays green while a real escalation path fails the build (invalid thresholds fail fast at parse time, exit 2). Posture gate (--min-score n) — the third verdict, scored instead of counted: exit 3 while the hardening score sits under the floor (--quiet --min-score 70 fails until the host scores 70+), the perfect companion to the --baseline score trajectory — harden the box, watch score 40 → 85, and the gate goes green on its own:

--min-score: the score-40 host fails the 70 floor with exit 3 and passes the 30 floor with exit 0

All three gates share the exit-3 contract and compose; the policy and regression verdicts (which name their finding) take message precedence over the score verdict. Compose the set: --parallel for speed, --sarif for the dashboard, --fail-on-new=high for the regression verdict, --min-score 70 for the posture floor, --report/--html with the hardening plan for the fix list.

Shell completion (--completion bash|zsh|fish) — tab-completion for every flag AND for the values that have a closed set (--risk <TAB> offers safe|low|medium|high|danger, --vector <TAB> walks the 26 vectors + all, --explain <TAB> lists the sources, --ignore <TAB> completes the source vocabulary, --min-risk <TAB> offers the four floors), all generated from the same tables the binary registers and validates against: a new flag lands in the next completion automatically, a new vector appears in its value list, and the script can never offer something the binary rejects. autoprivilege --completion bash >> ~/.bashrc and every flag documents itself as you type.

Hardening playbook (--explain cron, --explain all) — the remediation half of the loop: for every finding source, what it means and the exact steps that close it, safest first. The JSON voice (--explain cron --json, --explain all --json) hands the same playbooks to dashboards and hardening trackers as structured documents — scope, source, what, steps — never inventing advice for a source it does not know. The same playbook ships inside every --report/--html as a per-source section built from the sources the scan actually detected:

--explain cron: the CRON hardening playbook

Real usage gallery

Every capture below is a live run of the real binary — the rootless lab (v2.0.0), a bare-metal host scan, and the machine voices. Nothing is staged, edited or simulated:

🔎 A real host scan — 4 informational notes, 0 false positives (score 94)

The v2.0.0 audit rework in one picture: autoprivilege --color on a real (non-lab) Linux host. The old build flagged kernel heuristics, container context and passworded sudo rules as exploitable HIGHs — three gates would have tripped on a machine with no escalation path. Now each one is an informational note with its lead preserved (--min-risk low or --json surfaces them), the score reads a believable 94, and the run exits 0:

Real host scan: 4 informational findings, 0 exploitable, score 94, exit 0

🧪 The rootless lab — 16 seeded findings, score 0, honest risk spread

lab/rootless_lab.sh --color builds the fake vulnerable box inside nested user namespaces and scans it as labuser (uid 1000) — the honest privesc perspective. Sixteen findings across writable cron, readable /etc/shadow, SUID python3, cap_setuid and planted credentials; the score bottoms out at 0 and the risk bar spreads 1 SAFE / 4 MEDIUM / 10 HIGH / 1 DANGER. Nothing touched the real host:

Rootless lab scan: 16 findings, score 0, risks 1/4/10/1

📋 The dry-run plan — safest-first ordering with a risk cap

lab/rootless_lab.sh --exploit --dry-run --risk=medium shows exactly what the auto-exploit phase would run: 13 vectors enumerated, the SUID python3 technique scheduled first, every technique above the risk cap skipped and said so. Zero commands executed:

Dry run: execution plan, 13 vectors, python SUID first, high-risk skipped

🚦 The policy gate — --fail-on high trips with exit 3

lab/rootless_lab.sh --fail-on high on the vulnerable box: 10 exploitable HIGH findings, the gate names its verdict and the process exits 3 — the contract a CI pipeline fails on. The same flag on the clean host above exits 0:

Policy gate: fail-on high trips on 10 exploitable HIGHs, exit 3

🗂️ The JSON voice — --json --quiet | jq .summary

The same real host scan as the first capture, read by a machine: one JSON document on stdout, the summary object carrying the counts, the score and the honest rooted: false:

JSON summary via jq: 4 findings, score 94, rooted false

🎚️ The risk floor — --min-risk low quieting a loud host

lab/rootless_lab.sh --min-risk low on the same lab: the SAFE note drops out of every downstream view — terminal, JSON, reports and gates all see the identical filtered set:

Min-risk low: 15 findings after the floor, gates see the filtered reality

The rootless lab

AUTOPRIV demo: scan, dry-run plan, SUID escalation to uid=0 in the rootless lab

The lab is a fake compromised box built inside a user namespace (unshare -r -m): a temporary /etc with writable passwd/shadow/cron, a bind-mounted /usr/bin seeded with SUID python3 and find. The SUID bits only grant the namespace's mapped root — never yours. It is the safest way to demo, test and screenshot the full scan → enumerate → root cycle without Docker or any privileged setup:

lab/rootless_lab.sh                             # scan only
lab/rootless_lab.sh --exploit --dry-run         # show the plan
lab/rootless_lab.sh --exploit --risk=danger     # full climb to uid=0
lab/rootless_lab.sh --shell                     # interactive namespace shell
lab/rootless_lab.sh --seeds                     # list the staged vulnerabilities

What the lab stages (see --seeds): SUID python3 + find in /usr/bin, a writable root cron job in /etc/cron.d/backup, writable /etc/passwd, an owned (readable + writable) /etc/shadow, a writable vuln.service systemd unit, and a world-writable PATH directory as binary-planting bait. Everything is FAKE and lives only inside the namespace.

Docker testing

Four containers cover the matrix: vulnerable (should find vectors), clean (should find none), edgecases (weird perms, symlinks), and autoprivilege-test (runs the whole suite):

cd docker && docker compose up --build
./docker/test_runner.sh

Safety and ethics

AUTOPRIV is for authorized security work only: your own machines, labs, CTFs and engagements with written permission. It changes nothing by default; exploitation only happens behind --exploit, and even then it prefers reversible techniques and stops at the risk you allowed. Do not run it on systems you do not own or are not explicitly authorized to test.

Testing and CI

185 unit tests cover the tricky parts on purpose. The suite is an audit trail: every group below pins something that actually broke at some point, so it can never silently break again.

Scanners and parsers

  • Banner art decode-verified rune by rune (no misspelled ASCII art); vector CSV parsing, risk sorting, kernel-CVE ranges, sudo version ranges, exploit timeouts, shell-quoting regressions, spool guards, hash formats and markdown escaping
  • The recursive SUID/SGID walk: recursion, symlink skip, dedup, depth guard and the lib64 roots; honest SGID classification with declared technique provenance
  • Container-runtime heuristics: cgroup evidence, socket targeting, breakout vectors, privileged/PID-namespace detection
  • The preload/sudoers scanners (entry counting, writable-vs-informational honesty, idempotent sudoers write with newline compensation, per-file drop-ins gated on a locked directory); the group and login-hook scanners (writable-vs-silence honesty, shape-mismatch guard); the configurable scan timeout
  • The credential sweep of config directories (NetworkManager psk= / per-version PostgreSQL trees were silently dead before)

Reports and machine output

  • The --output JSON file (shape and 0600 perms); the markdown report's summary section mirroring the JSON counts; the HTML report (XSS escaping of finding text, self-contained with zero external references, 0600, diff section, empty-safe hardening plan); the SARIF export (rule dedup in first-appearance order, level mapping, path-only locations)
  • Atomic report writes (temp file + rename) — a crash mid-scan can never leave a truncated artifact for the next --baseline or gate to misread; fail-fast output directories (parents of --output/--report/--html/--sarif created 0700 before the scan)
  • The --parallel engine: byte-identical merge under out-of-order completion, stealth forces sequential

Gates, catalogs and the CLI surface

  • The policy gate --fail-on (threshold parsing, exploitable-only counting); the regression gate --fail-on-new (bare vs =value parsing, new-informational does not trip, reworded keys are not new, optional risk threshold honored); the posture gate --min-score (does not trip exactly at the floor, disabled at 0, lowest message precedence)
  • The baseline diff: new/resolved classification keyed by source+target, JSON shape without nulls, fail-fast validation of foreign JSON, score-trajectory rendering
  • The machine-readable catalogs (--list-vectors --json, --list-sources, --explain --json) pinned to the binary's own tables; the vector-selection symmetry table (every --vector name yields only its own category); GTFOBins sgid capture/persistence
  • Shell completion for bash/zsh/fish: every registered flag plus the closed value sets (vector names, sources, risk floors), all generated from the same tables the binary validates against — it can never offer something the binary rejects
  • The flags↔usage parity as a hermetic test (a private FlagSet built through the same registerFlags the binary uses — a new flag cannot ship undocumented, and usage cannot advertise a ghost); the hardening score (exact penalty weights, determinism, clamping); the exit-code decision as a pure function; the --min-risk floor, the --color capture flag, the --dry-run --json execution plan and the --top n window (bounds validated fail-fast); the version pinned to 2.0.0 by test

The v2.0 audit round (audit_fixes_test.go)

  • Root guards: the euid-0 scanners stay silent across all eight reworked scanner families
  • The tolerant getcap parser (both path = caps and modern path caps=ep shapes); the NSpid single-value-is-host correction
  • StackRot pinned as CVE-2023-3269 with both affected windows (6.1.0–6.1.36 and 6.3.0–6.3.10)
  • Kernel-CVE and PwnKit findings demoted to informational (Exploitable=false — heuristics never trip gates)
  • Sudo rule parsing with restricted-vs-free classification and comma-list sharing; the sudo/docker finding dedup by root cause
  • The baseline Ubuntu SUID set that must never cost score; renamed-SUID content-hash detection (a byte-identical copy of find stays a finding under any name); context-aware credential matching (assignments and cmd -p"…" shapes, not bare substrings); NFS rw-gated no_root_squash; /opt/custom config-path coverage; informational-cheap scoring weights

The CI pipeline runs on every push to main, three jobs:

  • build-and-test (Linux): build, go test -count=1, the same suite again under -race, go vet native and GOOS=windows (a Linux-only eye must never be the only one that saw the Windows code), gofmt check
  • lab-smoke (Linux): runs the real rootless lab and asserts --json --quiet stdout stays a single clean JSON document — a stray echo fails CI instead of silently breaking every jq-based consumer
  • windows (Windows runner): build, vet, the full test suite, and a live enumeration scan whose JSON contract is validated with PowerShell's own parser — the Windows binary is never a cross-compile-only artifact

License

MIT — see LICENSE.

About

AUTOPRIV — automated Linux privilege escalation: scan, enumerate, auto-root. 15 read-only scanners, 75 GTFOBins techniques, safest-first. Single Go binary, zero deps.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages