Skip to content
Go back

Linux Privilege Escalation: The Defensive Playbook

By SumGuy 7 min read
Linux Privilege Escalation: The Defensive Playbook

You’ve been compromised. The bad news: an attacker has shell access to your web server. The worse news: they’re running as www-data. The really bad news? They’re about to become root.

Here’s the thing about Linux privilege escalation: it’s not magic. It’s a methodology. An attacker gets a foothold at a low privilege level and methodically looks for ways to climb. They’ll poke at SUID binaries, check what you can run with sudo, hunt for world-writable files in system paths. Recent exploits like OpenClaw remind us that this is real—and it’s happening right now.

The good news? You can stop them. Not with firewalls. Not with IDS. With boring, unglamorous hardening. Let me walk you through the ladder they’re climbing and how to kick it down.

The Common Attack Vectors

SUID/SGID Binaries: The Trojan Horse

SUID (Set User ID) binaries are programs that run as their owner, regardless of who executes them. This is useful—sudo needs it, for instance. But if an attacker finds an SUID binary with a vulnerability, they just won with six characters of code.

First, find what you’ve got:

Terminal window
find / -perm -4000 -type f 2>/dev/null

That -4000 is the SUID bit. You’ll probably see sudo, mount, ping, maybe passwd. The question is: do you need all of them? A web server doesn’t need mount. A containerized app doesn’t need ping.

Defense: Audit your SUID binaries quarterly. Remove any that aren’t actively used. If you must keep them, restrict their execution with SELinux or AppArmor policies.

Sudo Misconfigurations: The Golden Ticket

The first thing an attacker does when they compromise a system is run sudo -l to see what they can execute as root without a password. If you’ve configured sudo carelessly—like allowing NOPASSWD: ALL for a service account—they’ve won.

Here’s what not to do:

/etc/sudoers (DO NOT USE THIS)
www-data ALL=(ALL) NOPASSWD: /usr/bin/systemctl
appuser ALL=(ALL) NOPASSWD: /bin/bash

This is signing your server over to the first attacker who compromises that user. If they get into www-data, one command and they’re root.

Defense: Audit /etc/sudoers with sudo visudo. Only grant specific commands (not wildcards). Require passwords for anything dangerous. Use sudo -l regularly on each service account to see what’s actually exposed.

Cron Jobs: The Root-Owned Time Bomb

If root runs a cron job that executes or sources a script you can write to, you’ve just become root. This happens more than you’d think—especially in hastily-assembled automation.

/root/cleanup.sh (VULNERABLE)
#!/bin/bash
rm -rf /tmp/cache/*
# ^ if /tmp/cache is world-writable, we have a problem

An attacker can replace /tmp/cache/malicious with executable code and wait for root’s cron job to run it.

Defense: If root must run a script, make sure all parent directories and the script itself are owned by root and writable only by root. Use find to check:

Terminal window
find /tmp -writable -not -user root 2>/dev/null

Better yet: use systemd timers instead of cron. They’re easier to audit and less prone to path-traversal issues.

Kernel Exploits: The Nuclear Option

A vulnerable kernel is a privilege escalation goldmine. Attackers use tools like linpeas.sh to check your kernel version against known CVEs. If you’re running something from 2022 with a known root-escape exploit, you’re getting rooted.

Defense: Keep your kernel patched. Run uname -r weekly. On Ubuntu, apt update && apt upgrade. On CentOS, yum update. Yes, it requires a reboot sometimes. Live with it.

Additionally, use seccomp profiles and gVisor (if you’re running containers) to limit the syscalls an attacker can make, even if they exploit the kernel.

Docker Socket Exposure: The Instant Root

If a low-privilege container user can reach /var/run/docker.sock, they can spawn a privileged container and mount the host’s filesystem. They’re root.

Terminal window
# Bad: www-data user can access docker socket
ls -la /var/run/docker.sock
# If www-data can read/write it, game over

Defense: The docker socket should only be accessible by root and the docker group. Remove untrusted users from the docker group. Better yet: use rootless Docker.

Capabilities Abuse: The Sledgehammer Approach

Linux capabilities let you grant specific privileges without full root access. But if you grant cap_sys_admin to a service, you’ve given them the keys to most of the kernel.

Terminal window
# Check capabilities on a binary
getcap /usr/bin/some_tool

Defense: Use setcap sparingly. Prefer the bare minimum capability needed. For example, ping only needs cap_net_raw, not cap_sys_admin. Review capabilities annually.

World-Writable Files: The Obvious Win

If /usr/local/bin/important_script.sh is writable by anyone, an attacker will replace it.

Terminal window
find / -writable -type f -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null | head -20

Defense: Run this command monthly. Remove write permissions from files in system paths:

Terminal window
chmod o-w /usr/local/bin/important_script.sh
chmod g-w /usr/local/bin/important_script.sh

Weak Service Accounts: Unnecessary Root Privileges

Some services run as root for historical reasons. Nginx, Postgres, and most application servers can run as unprivileged users and use capabilities for the few operations they need.

Defense: Audit your running services:

Terminal window
ps aux | grep -E "root.*nginx|root.*postgres|root.*java"

If you see unnecessary root services, reconfigure them to use dedicated unprivileged accounts.

Tools for Offense (and Defense)

Run these yourself to find what an attacker would find:

linpeas.sh — A comprehensive privilege escalation scanner that checks all of the above:

Terminal window
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | bash

linenum.sh — Another solid scanner, simpler than linpeas:

Terminal window
curl -L https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh | bash

Run these on your own infrastructure as part of your quarterly hardening review.

The Quick Hardening Audit

Save this script and run it monthly:

audit_privesc.sh
#!/bin/bash
set -e
echo "=== SUID Binaries ==="
find / -perm -4000 -type f 2>/dev/null | wc -l
echo "Above 5? Consider auditing."
echo ""
echo "=== Sudo Entries (Potentially Dangerous) ==="
sudo grep -E "NOPASSWD|ALL=\(ALL\)" /etc/sudoers 2>/dev/null || echo "None found (good)."
echo ""
echo "=== World-Writable Files (First 10) ==="
find / -writable -type f -not -path "/proc/*" -not -path "/sys/*" -not -path "/tmp/*" 2>/dev/null | head -10
echo ""
echo "=== Running Services As Root ==="
ps aux | grep "^root" | grep -v "kthread" | wc -l
echo "More than 10? Investigate which can be deprivileged."
echo ""
echo "=== Kernel Version ==="
uname -r
echo ""
echo "=== Docker Socket Permissions ==="
ls -la /var/run/docker.sock 2>/dev/null || echo "Docker not running (good)."

The Defensive Checklist

The Real Talk

Privilege escalation isn’t a single vulnerability. It’s an attacker methodology. They’ll try ten vectors before one works. Your job is to close as many as possible. You won’t get them all—but you’ll make their life expensive enough that they move to softer targets.

The OpenClaw CVE is fresh. The next one is coming. The difference between “completely owned” and “an attacker got stuck at low privilege” is the work you do right now, when everything’s running fine.

Go run that audit script. You’ll probably find something.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Proxmox NAT Bridge: One IP, Many VMs
Next Post
Adding Extra Swap to Linux

Related Posts