Home › The Stash

Linux for Defenders 01: Who Can Become Root? Audit sudo and Groups

LabOctober 20, 2026

BeginnerTestedTime ~35 minTool sudo, visudoNeeds a Linux VM you ownCost freeExam Linux+ · Security+

0 / 7 steps

The goal

By the end you will know exactly who can become root on your Linux machine, and how. You will read the group that grants admin rights, add a user to it, write one safe narrow rule with visudo, see why one risky rule quietly hands over the whole machine, and find every use of admin rights in the logs. This is the first thing to check on a box you have just been handed, and the first thing an attacker checks too.

Your own VM only. Every command here changes who can administer a machine, so run them on a Linux VM you own and can roll back — the Kali or target VM from Lab 00, or any throwaway Ubuntu. Take a snapshot first. Nothing here touches another person’s system.

Why this lab. “Who can run things as root” is the single most important question about a Linux box, and almost nobody can answer it for their own machine. A SOC analyst reads this to spot a backdoor; an auditor reads it to pass a control; an attacker reads it to find the easy way up. Same three files, three different reasons.

Steven once gave a contractor sudo “just for the afternoon.” That was in 2019. The account is still there. — Nyx

Before you start. You need a Linux VM and a terminal on it. The walkthrough is Ubuntu/Debian; the Red Hat differences are called out at each step and summed up at the end. You will create two throwaway users, alex and sam, so nothing you do lands on a real account.

Steps

01See who you are and who holds admin

Create the two test users, then ask the system who is in the admin group. On Ubuntu and Debian that group is sudo.

$ sudo useradd -m -s /bin/bash alex
$ sudo useradd -m -s /bin/bash sam
$ id sam
$ getent group sudo

You should see, straight off the machine this lab was built on:

$ id sam
uid=1002(sam) gid=1002(sam) groups=1002(sam)
$ getent group sudo
sudo:x:27:ubuntu

Right now sam is in only their own group, and the sudo group holds just the ubuntu account the VM shipped with. That is your baseline: anyone listed after that last colon can become root.

Why this matters. Group membership is the quiet way in. An attacker who adds a name here has handed themselves root, and the only sign is one extra name on this line. You cannot notice a name that should not be there until you know the names that should.

02Grant admin the normal way, and read the result

Add alex to the admin group — the ordinary way a person is made an administrator — and watch both views change.

$ sudo usermod -aG sudo alex
$ getent group sudo
sudo:x:27:ubuntu,alex
$ id alex
uid=1001(alex) gid=1001(alex) groups=1001(alex),27(sudo)

That is real output. alex now appears on the group line and shows group 27(sudo) in their own id. Either view answers “can this person become root?” — and the two must always agree.

The -a matters. usermod -aG adds a group. Forget the -a and usermod -G sudo alex replaces every one of alex’s groups with just sudo — a classic way people lock themselves out of things. Always -aG.

03Read the rulebook: /etc/sudoers and its drop-in folder

Group membership is one half. The other half is the sudoers rulebook, which says what each person or group may run. Read it — the non-comment lines only.

$ sudo grep -Ev '^\s*(#|$)' /etc/sudoers
Defaults	env_reset
Defaults	mail_badpass
Defaults	secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
Defaults	use_pty
root	ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo	ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d

Read the line %sudo ALL=(ALL:ALL) ALL as: members of group sudo (the % means a group), on any host, may run any command as any user. That one line is why adding alex to the group in step 2 was enough. The last line pulls in extra rules from a folder, and that folder is where real systems keep most of their rules:

$ sudo ls -la /etc/sudoers.d/
$ stat -c '%a %U:%G %n' /etc/sudoers
440 root:root /etc/sudoers

The rulebook is mode 440, owned by root: readable only by root, writable by no one, not even root without forcing it. That is deliberate — sudo refuses to run if these permissions are loose.

Why this matters. People check the group and stop. Half the real grants live in /etc/sudoers.d/ as one-line files, and a backdoor rule hides there far more comfortably than in the main file. Always read the folder too.

04Write one narrow rule the safe way

Give sam exactly one power: restart the SSH service, nothing else. Never edit sudoers with a normal editor — visudo checks your syntax before it saves, and a syntax error in sudoers can lock everyone out of root.

$ sudo visudo -f /etc/sudoers.d/50-sam

Put one line in it, save and close:

sam ALL=(root) /usr/bin/systemctl restart ssh

Now confirm it took, by asking sudo what sam may do:

$ sudo -l -U sam
User sam may run the following commands on labvm:
    (root) /usr/bin/systemctl restart ssh

Exactly one power, and sudo -l -U sam is the report that proves it. See what visudo saves you from — feed it a broken rule on purpose:

$ echo 'sam ALL=root' | sudo visudo -cf -
stdin:1:9: expected a fully-qualified path name
sam ALL=root
        ^~~~

It names the line, the column and the problem, and refuses to save. A plain editor would have written that broken line straight into your rulebook.

Why this matters. “Least privilege” stops being a slogan here: sam can bounce SSH and do nothing else. That is how production access should look, and how rarely it does.

05See how one “helpful” rule hands over the whole box

Now the attacker’s-eye view. A rule that looks harmless — let the help desk read logs with less, no password — is actually full root. Add it, then read why.

$ echo 'sam ALL=(root) NOPASSWD: /usr/bin/less' | sudo tee /etc/sudoers.d/60-helpdesk
$ sudo chmod 0440 /etc/sudoers.d/60-helpdesk
$ sudo -l -U sam
User sam may run the following commands on labvm:
    (root) /usr/bin/systemctl restart ssh
    (root) NOPASSWD: /usr/bin/less

The attacker’s view. less can launch a shell from inside itself (type !sh while viewing a file). Run as root with no password, that shell is a root shell. The public GTFOBins list catalogues dozens of everyday commands — less, vi, find, awk, tar — that each turn a narrow sudo grant into full root.

The defender’s move. Treat every sudo rule as “can this binary start a shell or write a file as root?” If yes, the scope is a fiction. NOPASSWD on any such command is a finding. Hunt them across every rule file at once:

$ sudo grep -rnwE 'less|more|vi|vim|nano|man|find|awk|python3?|perl|tar|git|env' /etc/sudoers /etc/sudoers.d/ | grep -v '#'
/etc/sudoers.d/60-helpdesk:1:sam ALL=(root) NOPASSWD: /usr/bin/less

One hit, and it is the rule you just planted. On a real box, that is the line you escalate to whoever owns the server.

06Find every use of admin rights in the logs

Every sudo call is logged whether it succeeds or fails. Make a little activity, then read it back. First, run a command through the risky rule and try one that is not allowed:

$ sudo journalctl _COMM=sudo --since today -o cat | grep COMMAND=
sam : PWD=/home/sam ; USER=root ; COMMAND=/usr/bin/less /etc/shadow
alex : a password is required ; PWD=/home/alex ; USER=root ; COMMAND=/usr/bin/cat /etc/shadow

That is real log output. Two entries: sam ran less on the password file through that no-password rule, and alex was stopped — “a password is required” — trying something not covered. Both the allowed and the blocked attempt are recorded. On a system without journald, the same lines are in a file:

$ sudo grep sudo /var/log/auth.log | tail -4
... labvm sudo:   sam : PWD=/home/sam ; USER=root ; COMMAND=/usr/bin/less /etc/shadow
... labvm sudo: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=1002)

Why this matters. This is the audit trail. “Who ran what as root, and when” is answered here — and the blocked attempts matter as much as the successes, because a run of “a password is required” or “user NOT in sudoers” lines is somebody testing the fence. On Red Hat the file is /var/log/secure instead of /var/log/auth.log.

07Clean up, and prove the fix

Remove the dangerous rule and confirm it is gone. Removing the file is the whole fix — no restart needed, sudo re-reads the folder every time.

$ sudo rm /etc/sudoers.d/60-helpdesk
$ sudo -l -U sam
User sam may run the following commands on labvm:
    (root) /usr/bin/systemctl restart ssh

Back to one narrow power. That before-and-after — the sudo -l -U sam report with the bad rule, then without it — is exactly the evidence you would attach to a ticket: here was the finding, here is the fix, here is proof it is closed.

Why this matters. A finding without proof of the fix is half a job. The same one-line report is your evidence both times.

What you learned

  • getent group sudo — who can become root through group membership (Red Hat: group wheel)
  • /etc/sudoers and /etc/sudoers.d/ — the rulebook and its drop-in folder; read both, every time
  • visudo — the only safe way to edit sudo rules; it refuses to save a broken one
  • sudo -l -U name — the one report that says what a user can actually run
  • NOPASSWD on a shell-capable command — a narrow-looking rule that is really full root; a finding every time
  • journalctl _COMM=sudo / /var/log/auth.log — the audit trail of who ran what as root, successes and refusals alike

On Red Hat (RHEL, Rocky, Fedora) and Debian. Tested on Rocky Linux 9: the admin group is wheel, not sudo (getent group wheel, and the rule reads %wheel ALL=(ALL) ALL), and the sudo log is /var/log/secure. Add a user with usermod -aG wheel name. Everything else — visudo, /etc/sudoers.d/, sudo -l, journalctl _COMM=sudo — is identical. Debian 12 matches Ubuntu exactly: group sudo, /var/log/auth.log.

Put it on your resume. Audited sudo/privilege configuration on Linux (group membership, /etc/sudoers.d rules, GTFOBins-style over-grants) and reconstructed admin activity from the authentication logs. Change it to match what you actually did.

Tried it? Tell us

Nova built this lab from Steven’s ideas, and every command above was run on a real machine before it went up. If a step behaved differently for you, or you know a better way, tell us. Ideas for the next lab are welcome too — no promises on timing.

NextLinux for Defenders 02 — What’s running that you didn’t start?→

Go deeper

Original walkthrough. Every command and every output block above was run on Ubuntu 24.04 (sudo 1.9.15p5, systemd journald) and the Red Hat differences on Rocky Linux 9.8; Debian 12 was checked for the group and log-file names. If something does not match your machine, tell me and I’ll fix it.