Privilege escalation with sudo and su

Listen to this lesson

Episode 22 · 53:57

This episode is a study companion for CompTIA Linux+ XK0-006 and is not produced by or endorsed by CompTIA.

Objective 3.3 · Security · 18% of the exam

Why this matters

Nobody should log in as root. Everybody occasionally needs to do something only root can do. sudo resolves that tension: named users run named commands with elevated privilege, and every use is logged against the person who invoked it.

Getting the configuration right matters more than it appears. A sudoers rule that looks tight can hand out a full root shell in one step, and a syntax error in that file can lock every administrator out of privilege on the machine — which is why there is a dedicated editor for it.

The lesson

su versus sudo

su switches user by authenticating as the target. su - becomes root by asking for root's password.

su                # become root, KEEPING your environment and directory
su -              # become root with a full LOGIN shell — root's env and PATH
su - alice        # become another user

The hyphen matters. Without it you keep your own PATH, environment and working directory, so root's sbin directories may be missing and you get "command not found" for tools that are plainly installed. su - gives you root's environment as though root had logged in, which is nearly always what you want.

sudo runs a single command as another user, authenticating as yourself. That difference is the whole argument for it:

  • No shared root password. Nobody needs to know it; it can stay unset.
  • Per-user revocation. Remove someone's sudo access without changing a password everyone else uses.
  • Attribution. The logs record who ran what, rather than "root did it".
  • Least privilege. Grant three commands instead of the whole machine.

Most current distributions ship with root's password locked precisely so sudo is the only route.

sudo command
sudo -u postgres psql     # as a specific user, not root
sudo -i                   # an interactive root LOGIN shell
sudo -s                   # a root shell keeping your environment
sudo -l                   # what am I allowed to run?
sudo -k                   # forget the cached credential now

sudo -l is genuinely useful on a machine you have inherited: it prints exactly what the current user may do, straight from the parsed policy, rather than requiring you to read sudoers and reason about precedence.

sudo -i versus sudo -s: -i simulates a full login as root, running root's profile and starting in root's home. -s starts a root shell but keeps your environment. -i is the cleaner choice, for the same reason su - beats su.

By default sudo caches your authentication for about fifteen minutes per terminal, which is why it does not ask every time.

The wheel and sudo groups

Rather than listing users individually, membership of a group grants access. The group differs by family:

  • wheel — Red Hat, Fedora, CentOS, and traditional Unix
  • sudo — Debian and Ubuntu
usermod -aG wheel alice      # RHEL family
usermod -aG sudo alice       # Debian family
groups alice                 # confirm

Remember -aG and not -G: without the a you replace every supplementary group the user has, which is a memorable way to remove somebody from sudo while trying to add them to it.

Group membership is evaluated at login, so the user must start a new session before it takes effect.

/etc/sudoers, and never editing it directly

visudo                                  # ALWAYS use this
visudo -c                               # check syntax without editing
visudo -f /etc/sudoers.d/deploy         # edit a drop-in file safely

visudo validates the file before saving. A syntax error in sudoers makes sudo refuse to run at all — and if root's password is locked, as it is by default on Ubuntu, nobody can fix it without rescue media. visudo catches the error and offers to re-edit, which is the entire reason it exists. Editing /etc/sudoers in a plain editor is the mistake this tool was built to prevent.

Rule syntax:

user    host = (runas_user:runas_group)    commands

root        ALL=(ALL:ALL) ALL
%wheel      ALL=(ALL)     ALL
%sudo       ALL=(ALL:ALL) ALL
alice       ALL=(ALL)     /usr/bin/systemctl restart nginx
bob         ALL=(postgres) /usr/bin/psql
deploy      ALL=(ALL)     NOPASSWD: /usr/local/bin/deploy.sh

% prefixes a group. The (runas) field says who you may become — omit it and root is assumed.

/etc/sudoers.d/ is the right place for your own rules. Drop-in files are not overwritten by package updates, can be managed by configuration management, and can be removed cleanly. Files with a . or ~ in the name are ignored, which catches people using deploy.conf.

NOPASSWD, and what it really costs

deploy  ALL=(ALL) NOPASSWD: /usr/local/bin/deploy.sh

NOPASSWD skips the password prompt. It is necessary for automation — a CI runner cannot type a password — and it is the tag that turns a narrow grant into a broad one if the command is chosen carelessly.

The rule to internalise: a NOPASSWD grant is only as narrow as the command cannot be talked out of. Several ordinary commands will hand over a shell:

alice ALL=(ALL) NOPASSWD: /usr/bin/vim        # :!bash  → root shell
alice ALL=(ALL) NOPASSWD: /usr/bin/find       # find . -exec /bin/sh \;
alice ALL=(ALL) NOPASSWD: /usr/bin/less       # !bash from the pager
alice ALL=(ALL) NOPASSWD: /usr/bin/awk        # awk 'BEGIN{system("/bin/sh")}'
alice ALL=(ALL) NOPASSWD: /bin/chmod          # chmod u+s /bin/bash

Every one of those is effectively ALL=(ALL) NOPASSWD: ALL written the long way. Anything with a shell escape, an -exec option, or the ability to write arbitrary files is a full grant. Prefer purpose-built scripts you control, and be wary of wildcards — /usr/bin/systemctl restart * permits restarting anything, including services whose restart runs code you specify.

NOEXEC is the counter-measure. It stops the permitted command from executing anything else, by preventing it from spawning child processes:

alice ALL=(ALL) NOEXEC: /usr/bin/less

With NOEXEC, less's !bash escape simply fails. It does not fix every case — a command that writes files can still be abused — but it closes the shell-escape route cleanly, and it is the correct answer when you must grant a command that has one.

Other tags and settings

Defaults        timestamp_timeout=5     # minutes to cache authentication
Defaults        logfile=/var/log/sudo.log
Defaults        requiretty              # refuse to run without a terminal
Defaults:alice  !authenticate           # alice never authenticates — avoid
Defaults        env_reset               # start from a clean environment
Defaults        secure_path="/usr/sbin:/usr/bin:/sbin:/bin"

env_reset and secure_path are security features, and they explain a common confusion: a command that works for you fails under sudo because sudo deliberately discards your PATH and environment. That is the point — it stops an attacker who can set your PATH from having sudo run their binary instead of the real one. Use full paths under sudo rather than fighting it.

Every use is logged to /var/log/secure (RHEL) or /var/log/auth.log (Debian): who ran what, from where, as whom. That record is the accounting half of access control, and it is why "log in as root" loses information that sudo preserves.

On the exam

  • su authenticates as the target user; sudo authenticates as yourself. That is why sudo needs no shared root password.
  • su - (and sudo -i) give a full login environment; without the hyphen you keep your own PATH.
  • Always edit with visudo, which validates before saving. A broken sudoers can leave nobody able to escalate.
  • %wheel on RHEL, %sudo on Debian.
  • Put custom rules in /etc/sudoers.d/, and avoid . in the filename.
  • NOPASSWD on a command with a shell escape — vim, less, find, awk — is equivalent to full root. NOEXEC blocks that route.
  • sudo -l lists what the current user may run.
  • sudo resets the environment and uses secure_path on purpose.

Practise what you just read

1. Why does sudo allow an organisation to avoid sharing the root password?

Select one

  1. sudo uses a separate password stored in /etc/sudoers
  2. sudo runs commands without any authentication
  3. sudo authenticates the invoking user with their own password
  4. sudo authenticates against the target account, as su does
Show answer

C. su asks for the TARGET account's password, so everyone who may become root must know the root password -- and it can never be changed without telling them all. sudo asks for YOUR password and checks a policy, so access is granted and revoked per person and every command is logged with a name attached.

2. Which command should always be used to edit /etc/sudoers?

Select one

  1. visudo, which validates the syntax before saving
  2. nano /etc/sudoers, since it cannot introduce control characters
  3. sudoedit /etc/sudoers, which locks the file
  4. vi /etc/sudoers, after taking a backup
Show answer

A. visudo locks the file against concurrent edits and refuses to save a syntactically invalid one. That matters because a broken sudoers file means sudo refuses to run at all, and if root login is disabled -- which it usually is -- nobody can escalate to fix it. The recovery is single-user mode or a rescue boot.

3. What is the difference between su and su - ?

Select one

  1. The hyphen restricts the session to a single command
  2. The hyphen starts a login shell
  3. The hyphen suppresses the password prompt entirely
  4. The hyphen keeps the current working directory
Show answer

B. Without the hyphen you become the target user but keep your own environment, including your PATH and working directory -- which is why root's sbin directories are sometimes missing and commands appear not to exist. su - reads the target's profile and starts a clean login shell. sudo -i is the sudo equivalent, and sudo -s is the halfway case.

5 more questions on this objective are part of the full course.

Practise the full question bank in the exam simulator

Hands-on labs

All hands-on labs