click the screen · press Enter
← back to blog
Kubernetes Journey · Topic 21

One Lazy Sudoers Line Is the Whole Privilege Escalation

Linux privilege escalation with sudo - Kubernetes Journey

Last topic I turned root login off on my node, which felt great for about ten minutes. Then I needed to install something and hit the obvious problem: if root can't log in, how does anyone do admin work? The answer is sudo, and it's a far better deal than logging in as root ever was.

It's also the first thing I check when I land on a Linux box in a lab. Not kernel exploits, nothing clever. Just sudo -l, because a good chunk of the time someone has left a rule in place that's more generous than they realised, and that single line is the whole privilege escalation. So this one covers both halves: how sudo works and how to set it up properly, then how a sloppy sudo rule quietly hands root to whoever goes looking.

What privilege escalation actually means

Privilege escalation is moving from a weaker account to a stronger one, nearly always root. Root is the account that can do anything on a Linux box: read any file, change any config, install anything, kill any process.

How it happens: two ways. The intended way, where you're allowed to and you ask politely with sudo. And the unintended way, where an attacker sitting on some low-privilege account finds a mistake in the setup and rides it up to root. Same destination, very different paperwork.

A normal user on a Kubernetes node is a nuisance for an attacker. Root on that node is a different story, because root can read the kubelet's config and credentials under /etc/kubernetes and /var/lib/kubelet, and those are the keys to talking to the cluster. On a node, escalation isn't just "they own this box", it's the step where one machine becomes a cluster-wide problem.

Escalation is the hinge. Everything before it is a foothold. Everything after it is an incident.

Sudo: borrowing root for one command

sudo (superuser do) runs one command as another user, and unless you say otherwise that user is root. You stay logged in as yourself throughout. You're borrowing the power for a single command, not moving in.

It does that in four steps, and they're worth knowing in order.

01
You ask

sudo apt install nginx

02
Rules checked

Are you in /etc/sudoers?

03
Your password

Yours, not root's

04
Runs and logs

Who ran what, and when

The clever bits are steps 3 and 4: nobody needs the root password, and everything leaves a trace.

Why it beats logging in as root: step three is the part people undersell. With sudo, the root password doesn't need to live in anyone's head or in a shared vault. Ten admins, ten separate passwords, and you revoke one by deleting one line. Compare that with a shared root password, where somebody leaves the company and you're rotating a credential across every box you own.

Step four gives you the audit trail. Every sudo command is logged with the user, the command and the time. If people just log in as root, all your logs say is "root did something", which is worth nothing at 2am when you're trying to work out who broke what.

The upshot is that sudo turns "who has root" into a list you control and a log you can actually read.

What it looks like without it

Fastest way to feel the difference. Try a package install as a normal user and you get stopped at the door, because the package manager needs to write to places only root owns.

the same command, with and without sudo
$ apt install nginx E: Could not open lock file /var/lib/dpkg/lock-frontend (13: Permission denied) E: Unable to acquire the dpkg frontend lock, are you root? $ sudo apt install nginx [sudo] password for bikram: Reading package lists... Done # one word of difference
"are you root?" is the permission check talking. Sudo answers yes, for exactly one command.

One thing that confuses everyone once: after you type your password, sudo remembers you for a few minutes (15 by default), so the next few commands don't prompt. It isn't broken, it's a timer. sudo -k clears it if you want to test the prompt properly.

Reading /etc/sudoers without breaking it

/etc/sudoers is the rulebook. It decides who can run what, as whom. No rule covering you (directly or through a group) means sudo just says no.

Each rule is four fields, and once you read them in order the file stops looking cryptic:

who     where=(as-whom)     what

mark    ALL=(ALL:ALL)       ALL
1. Who

A username, or a group with % in front. %sudo means everyone in the sudo group

2. Where

Which hosts the rule covers. On a single server, nearly always ALL

3. As whom

In brackets: which user, and after the colon which group, the command may run as

4. What

Which commands are allowed. ALL means anything, and this is the field that gets people

Read any sudoers line left to right in this order and it decodes itself.

So %sudo ALL=(ALL:ALL) ALL reads as "anyone in the sudo group, on any host, as any user or group, can run any command". That's the stock Ubuntu setup, and it's why adding someone to the sudo group is all it takes to make them an admin. A tighter rule names specific binaries instead, so a user can restart one service and nothing else:

# sarah can reboot the box, and that's it
sarah   ALL=(root) /usr/sbin/reboot

# deploy can restart nginx without typing a password
deploy  ALL=(root) NOPASSWD: /bin/systemctl restart nginx

That NOPASSWD is handy for scripts and CI, and it's also the flag I most often find over-applied. It's fine on one narrow command. It's a gift to an attacker on a broad one.

Edit it with visudo, never a plain editor. sudo visudo opens the file and syntax-checks it before saving. Get a typo into /etc/sudoers with a normal editor and sudo can stop working entirely. Pair that with root login disabled from last topic and you've locked every admin out of the box. I've done exactly this on a VM, and it's a grim five minutes even when the machine is disposable.
Lab scope Everything below runs on a throwaway Ubuntu VM I own. The escalation section exists so you can spot and fix these rules on your own systems. Don't point any of it at a machine you don't have permission to test.

Hands-on: grant sudo, then audit it

Let's create the rule, prove it works, then check it the way an attacker would. Four short steps.

Step 1, make a user with no privileges. This is our stand-in for a low-privilege account, the kind an attacker might land on through a web app or a stolen key.

sudo adduser testy
sudo -u testy sudo -l     # what can testy do? nothing yet

Step 2, grant a narrow rule with visudo. Rather than dropping testy in the sudo group (which grants everything), give one specific command. Run sudo visudo and add this at the bottom:

testy   ALL=(root) NOPASSWD: /bin/systemctl restart nginx

Step 3, check what the rule really grants. This is sudo -l, which lists everything the current user is allowed to run through sudo. It's the single most useful command in this whole post, and it reads the rules for you so you don't have to interpret the file by hand.

sudo -l # run as testy
Matching Defaults entries for testy on lab-node: env_reset, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin User testy may run the following commands on lab-node: (root) NOPASSWD: /bin/systemctl restart nginx # one command, no password. narrow, and that's the point
This is the first thing I run on any box in a lab. It tells you the escalation path in one go, no guessing.

Step 4, verify the boundary holds. The allowed command should work, and anything else should be refused. If both are true, your rule is doing its job.

sudo systemctl restart nginx   # allowed, runs
sudo cat /etc/shadow           # refused: "not allowed to execute"

Where narrow rules stop being narrow

Now the bit I find genuinely interesting, and the reason I don't fully trust a restricted sudo rule on sight. Allowing one command is not the same as allowing one action. If the command you allowed can start a shell, run other programs, or write files wherever it likes, then it's really a rule that says "become root, via this one slightly awkward door".

Editors are the obvious case. Plenty of them can drop you to a shell from inside, and if the editor is running as root through sudo, so is the shell you get. Same story for anything with a built-in "run this command" feature, and for interpreters like Python or Perl, which exist precisely to run whatever code you hand them.

"testy can edit that one config file with the editor, as root. That's all I've given them, so the blast radius is one file."
The editor runs as root. Most editors can spawn a shell or run a command from inside. That shell inherits root. So the rule grants full root, just with an extra keystroke on the way.
The gap between those two panes is where a lot of real-world privilege escalation lives.

There's a well-known project called GTFOBins that catalogues exactly this: ordinary Linux binaries and the ways each one can be abused when it's available through sudo or with special permissions. It's maintained by the community and it's the reference I check whenever I see a restricted rule. If the binary in your sudoers line has a page there, treat the rule as "full root" and design accordingly.

The honest version of the lesson: when you write a sudo rule, don't ask "what do I want them to do". Ask "what is the most this binary can be made to do, running as root". Those two answers are the same far less often than you'd hope.

Try itSudoers analyser: is this line a root shell in disguise?
find vim awk systemctl apt-get service (safe-ish) ALL
A parser plus a GTFOBins-style check, in your browser. Educational only, against boxes you own.

Auditing what you've already got

Most boxes have accumulated sudo rules over years, and nobody's read them since. Here's the sweep I do, and honestly the first two commands find most of it.

a quick sudo audit
$ sudo grep -rvE '^#|^$' /etc/sudoers /etc/sudoers.d/ # every live rule /etc/sudoers:%sudo ALL=(ALL:ALL) ALL /etc/sudoers.d/ci:jenkins ALL=(ALL) NOPASSWD: ALL # passwordless root $ getent group sudo # who's actually an admin? sudo:x:27:bikram,olduser,contractor2023 # a leaver still in the sudo group, and CI with unrestricted root
Don't forget /etc/sudoers.d/ (it's included by the main file). Plenty of rules get dropped in there by packages and CI setup, then forgotten.

On the logging side, sudo writes to /var/log/auth.log on Debian and Ubuntu, or /var/log/secure on RHEL-family systems. Two patterns are worth alerting on. The first is a failed sudo attempt, which shows up as "user NOT in sudoers" and means someone tried something they shouldn't. The second is any successful sudo by an account that shouldn't be doing admin work at all, like a service account.

# someone tried and was refused
sudo grep "NOT in sudoers" /var/log/auth.log

# everything that ran through sudo, with who and what
sudo grep "COMMAND=" /var/log/auth.log | tail

That second one is the payoff for using sudo instead of root logins in the first place. It's an actual list of who did what. Ship those lines off the box to somewhere central, because an attacker with root can edit local logs, and the same logic applies here as with cluster audit logging.

Sudo config review, lab-node
CheckStatusNote
NOPASSWD: ALL for CI accountFAILPasswordless root, scope it to real commands
Leaver still in sudo groupFAILRemove from the group today
Restricted rule on an editorWARNShell escape possible, treat as full root
Rules edited via visudoPASSSyntax checked before save
sudo logs shipped off-hostPASSSurvives a local root compromise
Flip "Failures only" for the to-do list. The top two are the ones I'd fix before finishing my coffee.

The shape I'd leave a node in

So the shape of it: root login stays off, admins get sudo, and every rule is as narrow as it can be while still being useful. Give people commands, not ALL. Be sparing with NOPASSWD. Use visudo every single time. And run sudo -l as your own users occasionally, because reading the rules as the system applies them beats reading the file and hoping you interpreted it right.

The thing I keep coming back to is that sudo isn't really a security control on its own, it's a delegation tool that's only as good as the rules you write. A tidy /etc/sudoers with two narrow entries is genuinely strong. The same file with one lazy NOPASSWD: ALL is a root shell with extra steps, and it'll pass a casual glance because it still looks like a restriction.

Where I'm still figuring things out: keeping this consistent across a fleet. Hand-editing sudoers on each node is fine for a lab and hopeless past about three machines, so it wants to live in config management alongside the SSH hardening from last topic. That's the piece I want to build next, and I haven't landed on the right approach yet, so if you've got a setup you like for managing sudo rules at scale, I'd like to hear it.

Further reading

The manual pages are the real source of truth here: man sudoers covers every option and the full rule syntax, and man sudo covers the flags. GTFOBins is the community reference for which binaries can be abused into a root shell, and it's worth a look before you write any restricted rule.

FAQ

What is privilege escalation in Linux?

Privilege escalation is going from a normal user to a more powerful one, usually root. Done on purpose it is just sudo letting you install a package. Done by an attacker it means turning a small foothold into full control of the machine, which on a Kubernetes node puts the whole cluster at risk.

What does sudo actually do?

Sudo runs a single command as another user, normally root. It checks the rules in /etc/sudoers to see if you are allowed, asks for your own password rather than the root password, runs that one command, and writes a log line saying who ran what and when.

How do I read a line in /etc/sudoers?

Every rule has four parts: who it applies to, which hosts it covers, which user the command runs as in brackets, and which commands are allowed. So mark ALL=(ALL:ALL) ALL means mark, on any host, as any user, can run anything. A percent prefix means a group, not a person.

Why should I edit sudoers with visudo?

Visudo checks your syntax before it saves. A single typo in /etc/sudoers can stop sudo working at all, and if root login is already disabled you have just locked everyone out of admin access on that box. Visudo catches the mistake and lets you fix it first.

Can a restricted sudo rule still give full root?

Often yes. If the allowed command can start a shell, run other programs or write files anywhere, an attacker escapes straight to root from it. Editors, interpreters and tools like find are the classic examples, catalogued by the GTFOBins project. Allowing one command is not the same as allowing one action.